home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / DTS.Lib / TextEditControl.c < prev    next >
Encoding:
Text File  |  1992-10-22  |  74.5 KB  |  2,852 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         texteditcontrol.c
  5. ** Written by:      Eric Soldan
  6. ** Based on:        TESample, by Bryan Stearns
  7. ** Suggestions by:  Forrest Tanaka, Dave Radcliffe
  8. **
  9. ** Copyright © 1990-1991 Apple Computer, Inc.
  10. ** All rights reserved.
  11. */
  12.  
  13. /* This is a control implementation of TextEdit.  The advantages to this are:
  14. **
  15. ** 1) Makes using TextEdit in a non-dialog window easy.
  16. ** 2) The TextEdit record is automatically associated with the window, since
  17. **    it is in the window's control list.
  18. ** 3) The TextEdit control can have scrollbars associated with it, and these
  19. **    are also kept in the window's control list.
  20. ** 4) Updating of the TextEdit record is much simpler, since all that is
  21. **    necessary is to draw the control (or all the window's controls with
  22. **    a DrawControls call).
  23. ** 5) There are simple calls to handle TextEdit events.
  24. ** 6) Undo is already supported.
  25. ** 7) A document length can be specified.  This length will not be exceeded
  26. **    when editing the TextEdit record.
  27. ** 8) When you close the window, the TextEdit record is disposed of.
  28. **    (This automatic disposal can easily be defeated.)
  29. **
  30. **
  31. ** To create a TextEdit control, you only need a single call.  For example:
  32. **
  33. **    CTENew(rViewCtl,            Resource ID of view control for TextEdit control.
  34. **           window,                Window to hold TERecord.
  35. **           &teHndl,                Return handle for TERecord.
  36. **           &ctlRect,            Rect for TextEdit view control.
  37. **           &destRect,            destRect for TERecord
  38. **           &viewRect,            viewRect for TERecord
  39. **           &borderRect,            Used to frame a border.
  40. **           32000,                Max size for TERecord text.
  41. **           cteVScrollLessGrow    TERecord read-write, with vertical scroll
  42. **                                that leaves space for grow box.
  43. **    );
  44. **
  45. ** If you create a TextEdit control that is read-only, you will not be able
  46. ** to edit it, of course.  There will also be no blinking caret for that
  47. ** TextEdit control.  You will be able to select text and copy to the
  48. ** clipboard, but that is all.
  49.  
  50. ** Simply create destRect, viewRect, and borderRect appropriately, and
  51. ** then call CTENew (which stands for Control TENew).  If teHndl is returned
  52. ** nil, then CTENew failed.  Otherwise, you now have a TextEdit control in
  53. ** the window.
  54. **
  55. ** NOTE: There is a TextEdit bug (no way!!) such that you may need to set the
  56. **       viewRect right edge 2 bigger than the right edge of destRect.  If you
  57. **       do not do this, then there will be some clipping on the right edge in
  58. **       some cases.  Of course, you may want this.  You may want horizontal
  59. **       scrolling, and therefore you would want the destRect substantially
  60. **       larger than the viewRect.  If you don't want horizontal scrolling,
  61. **       then you probably don't want any clipping horizontally, and therefore
  62. **       you will need to set destRect.right 2 less than viewRect.right.
  63. **
  64. **
  65. ** If the CTENew call succeeds, you then have a TextEdit control in your
  66. ** window.  It will be automatically disposed of when you close the window.
  67. ** If you don't waht this to happen, then you can detach it from the
  68. ** view control which owns it.  To do this, you would to the following:
  69. **
  70. **  viewCtl = CTEViewFromTE(theTextEditHndl);
  71. **  if (viewCtl) SetCRefCon(viewCtl, nil);
  72. **
  73. ** The view control keeps a reference to the TextEdit record in the refCon.
  74. ** If the refCon is cleared, then the view control does nothing.  So, all that
  75. ** is needed to detach a TextEdit record from a view control is to set the
  76. ** view control's refCon nil.  Now if you close the window, you will still
  77. ** have the TextEdit record.
  78. **
  79. **
  80. ** To remove a TextEdit control completely from a window, you make one call:
  81. **
  82. **  CTEDispose(theTextEditHndl);
  83. **
  84. ** This disposes of the TextEdit record, the view control, and any scrollbar
  85. ** controls that were created when the TextEdit control was created with
  86. ** the call CTENew.
  87. **
  88. **
  89. ** Events for TextEdit record are handled nearly automatically.  You can
  90. ** make one of 3 calls:
  91. **
  92. **  CTEClick(window, eventPtr, &action);
  93. **  CTEEvent(window, eventPtr, &action);
  94. **  CTEKey(window, eventPtr);
  95. **
  96. ** In each case, if the event was handled, true is returned.  CTEEvent simply
  97. ** calls either CTEClick or CTEKey, whichever is appropriate.
  98. **
  99. **
  100. ** Another call you will want to use is CTEEditMenu.  This is used to set the
  101. ** state of cut/copy/paste/clear for TextEdit controls.  It checks the active
  102. ** control to see if text is selected, if the control is read-only, etc.
  103. ** Based on this information, it sets cut/copy/paste/clear either active
  104. ** or inactive.  If any menu items are set active, it returns true.
  105. **
  106. **
  107. ** One more high-level call is CTEUndo().  In response to an undo menu item
  108. ** being selected by the user, just call CTEUndo(), and the edits the user
  109. ** has made will be undone.  (This includes undoing an undo.)
  110. **
  111. **
  112. ** The last high-level call is CTEClipboard.  Call it when you want to do a
  113. ** cut/copy/paste/clear for the active TextEdit control.  The value to pass
  114. ** is as follows:
  115. **
  116. **  2: cut
  117. **  3: copy
  118. **  4: paste
  119. **  5: clear
  120. **
  121. ** These are the same values you would pass to a DA for these actions.
  122. */
  123.  
  124.  
  125.  
  126. /*****************************************************************************/
  127.  
  128.  
  129.  
  130. #ifndef __TEXTEDITCONTROL__
  131. #include "TextEditControl.h"
  132. #endif
  133.  
  134. #ifndef __CONTROLS__
  135. #include <Controls.h>
  136. #endif
  137.  
  138. #ifndef __ERRORS__
  139. #include <Errors.h>
  140. #endif
  141.  
  142. #ifndef __EVENTS__
  143. #include <Events.h>
  144. #endif
  145.  
  146. #ifndef __MEMORY__
  147. #include <Memory.h>
  148. #endif
  149.  
  150. #ifndef __MENUS__
  151. #include <Menus.h>
  152. #endif
  153.  
  154. #ifndef __OSUTILS__
  155. #include <OSUtils.h>
  156. #endif
  157.  
  158. #ifndef __RESOURCES__
  159. #include <Resources.h>
  160. #endif
  161.  
  162. #ifndef __SCRAP__
  163. #include <Scrap.h>
  164. #endif
  165.  
  166. #ifndef __UTILITIES__
  167. #include "Utilities.h"
  168. #endif
  169.  
  170.  
  171.  
  172. /*****************************************************************************/
  173.  
  174.  
  175.  
  176. typedef struct cdefRsrcJMP {
  177.     long    moveInst;
  178.     long    jsrInst;
  179.     short    jmpInst;
  180.     long    jmpAddress;
  181. } cdefRsrcJMP;
  182. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  183.  
  184.  
  185.  
  186. /*****************************************************************************/
  187.  
  188.  
  189.  
  190. static pascal long    CTECtl(short varCode, ControlHandle ctl, short msg, long parm);
  191. static short        gViewID;
  192. static Boolean        gCanGoSlow;
  193.  
  194.  
  195.  
  196. /*****************************************************************************/
  197.  
  198.  
  199.  
  200. static TEHandle            gActiveTEHndl;
  201.     /* Currently active TextEdit record.  (nil if none active.) */
  202.  
  203. static TEHandle            gFoundTEHndl;
  204.     /* Global value used to return info from the TextEdit control proc. */
  205.  
  206. static ControlHandle    gFoundViewCtl;
  207.     /* Global value used to return info from the TextEdit control proc. */
  208.  
  209. static pascal void        VActionProc(ControlHandle scrollCtl, short part);
  210. static pascal void        HActionProc(ControlHandle control, short part);
  211. static void                AdjustOneScrollValue(TEHandle teHndl, ControlHandle ctl, Boolean vert);
  212.  
  213. ClikLoopProcPtr        gDefaultClikLoop;
  214.     /* The clikLoop TextEdit wants to use.  Our custom clikLoop must call
  215.     ** this as well.  The default TextEdit clikLoop is stored here.
  216.     */
  217.  
  218. #define kTELastForWind    1
  219. #define kCrChar            13
  220.  
  221.  
  222.  
  223. static void                dummyCTEActivate(Boolean active, TEHandle teHndl);
  224. static Boolean            dummyCTEClick(WindowPtr window, EventRecord *event, short *action);
  225. static void                dummyCTEClipboard(short menuID);
  226. static void                dummyCTEConvertClipboard(Boolean convertClipboard, Boolean becomingActive);
  227. static ControlHandle    dummyCTECtlHit(void);
  228. static void                dummyCTEDispose(TEHandle teHndl);
  229. static TEHandle            dummyCTEDisposeView(ControlHandle viewCtl);
  230. static short            dummyCTEDocHeight(TEHandle teHndl);
  231. static Boolean            dummyCTEEditMenu(Boolean *activeItem, short editMenu, short undoID, short cutID);
  232. static Boolean            dummyCTEEvent(WindowPtr window, EventRecord *event, short *action);
  233. static void                dummyCTEFakeClick(short newStart, short newEnd, Boolean extend, TEHandle teHndl);
  234. static TEHandle            dummyCTEFindActive(WindowPtr window);
  235. static Boolean            dummyCTEFindCtl(WindowPtr window, EventRecord *event, TEHandle *teHndl, ControlHandle *ctlHit);
  236. static TEHandle            dummyCTEFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl);
  237. static void                dummyCTEHide(TEHandle teHndl);
  238. static void                dummyCTEIdle(void);
  239. static short            dummyCTEKey(WindowPtr window, EventRecord *event);
  240. static void                dummyCTEMove(TEHandle teHndl, short newH, short newV);
  241. static void                dummyCTENewUndo(ControlHandle viewCtl, Boolean alwaysNewUndo);
  242. static ControlHandle    dummyCTENext(WindowPtr window, TEHandle *teHndl, ControlHandle ctl);
  243. static short            dummyCTENumTextLines(TEHandle teHndl);
  244. static short            dummyCTENumViewLines(TEHandle teHndl);
  245. static OSErr            dummyCTEPrint(TEHandle teHndl, short *offset, Rect *rct);
  246. static Boolean            dummyCTEReadOnly(TEHandle teHndl);
  247. static ControlHandle    dummyCTEScrollFromTE(TEHandle teHndl, Boolean vertScroll);
  248. static ControlHandle    dummyCTEScrollFromView(ControlHandle viewCtl, Boolean vertScroll);
  249. static void                dummyCTESetKeyFilter(TEHandle teHndl, CTEKeyFilterProcPtr proc);
  250. static void                dummyCTESetSelect(short start, short end, TEHandle teHndl);
  251. static void                dummyCTEShow(TEHandle teHndl);
  252. static void                dummyCTESize(TEHandle teHndl, short dx, short dy, Boolean newDest);
  253. static Handle            dummyCTESwapText(TEHandle teHndl, Handle newText, Boolean update);
  254. static WindowPtr        dummyCTETargetInfo(TEHandle *teHndl, Rect *teView);
  255. static void                dummyCTEUndo(void);
  256. static void                dummyCTEUpdate(TEHandle teHndl, ControlHandle ctl, Boolean justShowActive);
  257. static ControlHandle    dummyCTEViewFromTE(TEHandle teHndl);
  258. static Boolean            dummyCTEWindActivate(WindowPtr window);
  259. static void                dummyCTEAdjustTEBottom(TEHandle teHndl);
  260. static void                dummyCTEAdjustScrollValues(TEHandle teHndl);
  261.  
  262. CTEActivateProcPtr           gcteActivate           = dummyCTEActivate;
  263. CTEClickProcPtr              gcteClick              = dummyCTEClick;
  264. CTEClipboardProcPtr          gcteClipboard          = dummyCTEClipboard;
  265. CTEConvertClipboardProcPtr   gcteConvertClipboard   = dummyCTEConvertClipboard;
  266. CTECtlHitProcPtr             gcteCtlHit             = dummyCTECtlHit;
  267. CTEDisposeProcPtr            gcteDispose            = dummyCTEDispose;
  268. CTEDisposeViewProcPtr        gcteDisposeView        = dummyCTEDisposeView;
  269. CTEDocHeightProcPtr          gcteDocHeight          = dummyCTEDocHeight;
  270. CTEEditMenuProcPtr           gcteEditMenu           = dummyCTEEditMenu;
  271. CTEEventProcPtr              gcteEvent              = dummyCTEEvent;
  272. CTEFakeClickProcPtr          gcteFakeClick          = dummyCTEFakeClick;
  273. CTEFindActiveProcPtr         gcteFindActive         = dummyCTEFindActive;
  274. CTEFindCtlProcPtr            gcteFindCtl            = dummyCTEFindCtl;
  275. CTEFromScrollProcPtr         gcteFromScroll         = dummyCTEFromScroll;
  276. CTEHideProcPtr               gcteHide               = dummyCTEHide;
  277. CTEIdleProcPtr               gcteIdle               = dummyCTEIdle;
  278. CTEKeyProcPtr                gcteKey                = dummyCTEKey;
  279. CTEMoveProcPtr               gcteMove               = dummyCTEMove;
  280. CTENewUndoProcPtr            gcteNewUndo            = dummyCTENewUndo;
  281. CTENextProcPtr               gcteNext               = dummyCTENext;
  282. CTENumTextLinesProcPtr       gcteNumTextLines       = dummyCTENumTextLines;
  283. CTENumViewLinesProcPtr       gcteNumViewLines       = dummyCTENumViewLines;
  284. CTEPrintProcPtr              gctePrint              = dummyCTEPrint;
  285. CTEReadOnlyProcPtr           gcteReadOnly           = dummyCTEReadOnly;
  286. CTEScrollFromTEProcPtr       gcteScrollFromTE       = dummyCTEScrollFromTE;
  287. CTEScrollFromViewProcPtr     gcteScrollFromView     = dummyCTEScrollFromView;
  288. CTESetKeyFilterProcPtr       gcteSetKeyFilter       = dummyCTESetKeyFilter;
  289. CTESetSelectProcPtr          gcteSetSelect          = dummyCTESetSelect;
  290. CTEShowProcPtr               gcteShow               = dummyCTEShow;
  291. CTESizeProcPtr               gcteSize               = dummyCTESize;
  292. CTESwapTextProcPtr           gcteSwapText           = dummyCTESwapText;
  293. CTETargetInfoProcPtr         gcteTargetInfo         = dummyCTETargetInfo;
  294. CTEUndoProcPtr               gcteUndo               = dummyCTEUndo;
  295. CTEUpdateProcPtr             gcteUpdate             = dummyCTEUpdate;
  296. CTEViewFromTEProcPtr         gcteViewFromTE         = dummyCTEViewFromTE;
  297. CTEWindActivateProcPtr       gcteWindActivate       = dummyCTEWindActivate;
  298. CTEAdjustTEBottomProcPtr     gcteAdjustTEBottom     = dummyCTEAdjustTEBottom;
  299. CTEAdjustScrollValuesProcPtr gcteAdjustScrollValues = dummyCTEAdjustScrollValues;
  300.  
  301.  
  302.  
  303. /*****************************************************************************/
  304. /*****************************************************************************/
  305. /*****************************************************************************/
  306.  
  307.  
  308.  
  309. /* Instead of calling the functions directly, you can reference the global
  310. ** proc pointers that reference the functions.  This keeps everything from
  311. ** being linked in.  The default global proc pointers point to dummy functions
  312. ** that behave as if there aren't any TextEdit controls.  The calls can still be
  313. ** made, yet the runtime behavior is such that it will operate as if there
  314. ** no instances of the TextEdit control.  This allows intermediate code to access
  315. ** the functions or not without automatically linking in all sorts of stuff
  316. ** into the application that isn't desired.  To change the global proc pointers
  317. ** so that they point to the actual functions, just call CTEInitialize() once
  318. ** in the beginning of the application.  If CTEInitialize() is referenced, it will
  319. ** get linked in.  In turn, everything that it references directly or indirectly
  320. ** will get linked in. */
  321.  
  322. #pragma segment Controls
  323. void    CTEInitialize(void)
  324. {
  325.     if (gcteActivate != CTEActivate) {
  326.         gcteActivate           = CTEActivate;
  327.         gcteClick              = CTEClick;
  328.         gcteClipboard          = CTEClipboard;
  329.         gcteConvertClipboard   = CTEConvertClipboard;
  330.         gcteCtlHit             = CTECtlHit;
  331.         gcteDispose            = CTEDispose;
  332.         gcteDisposeView        = CTEDisposeView;
  333.         gcteDocHeight          = CTEDocHeight;
  334.         gcteEditMenu           = CTEEditMenu;
  335.         gcteEvent              = CTEEvent;
  336.         gcteFakeClick          = CTEFakeClick;
  337.         gcteFindActive         = CTEFindActive;
  338.         gcteFindCtl            = CTEFindCtl;
  339.         gcteFromScroll         = CTEFromScroll;
  340.         gcteHide               = CTEHide;
  341.         gcteIdle               = CTEIdle;
  342.         gcteKey                = CTEKey;
  343.         gcteMove               = CTEMove;
  344.         gcteNewUndo            = CTENewUndo;
  345.         gcteNext               = CTENext;
  346.         gcteNumTextLines       = CTENumTextLines;
  347.         gcteNumViewLines       = CTENumViewLines;
  348.         gctePrint              = CTEPrint;
  349.         gcteReadOnly           = CTEReadOnly;
  350.         gcteScrollFromTE       = CTEScrollFromTE;
  351.         gcteScrollFromView     = CTEScrollFromView;
  352.         gcteSetKeyFilter       = CTESetKeyFilter;
  353.         gcteSetSelect          = CTESetSelect;
  354.         gcteShow               = CTEShow;
  355.         gcteSize               = CTESize;
  356.         gcteSwapText           = CTESwapText;
  357.         gcteTargetInfo         = CTETargetInfo;
  358.         gcteUndo               = CTEUndo;
  359.         gcteUpdate             = CTEUpdate;
  360.         gcteViewFromTE         = CTEViewFromTE;
  361.         gcteWindActivate       = CTEWindActivate;
  362.         gcteAdjustTEBottom     = CTEAdjustTEBottom;
  363.         gcteAdjustScrollValues = CTEAdjustScrollValues;
  364.     }
  365. }
  366.  
  367.  
  368.  
  369. /*****************************************************************************/
  370.  
  371.  
  372.  
  373. /* Activate this TextEdit record.  If another is currently active, deactivate
  374. ** that one.  The view control for this TextEdit record is also flagged to
  375. ** indicate which was the last active one for this window.  If the previous
  376. ** active TextEdit record was in the same window, then flag the old one off
  377. ** for this window.  The whole point for this per-window flagging is so that
  378. ** activate events can reactivate the correct TextEdit control per window. */
  379.  
  380. #pragma segment Controls
  381. void    CTEActivate(Boolean active, TEHandle teHndl)
  382. {
  383.     WindowPtr        tePort, oldPort;
  384.     ControlHandle    viewCtl;
  385.     TEHandle        te;
  386.     CTEDataHndl        teData;
  387.  
  388.     if (!teHndl) return;
  389.  
  390.     if (!active) {
  391.         GetPort(&oldPort);
  392.         SetPort(tePort = (*teHndl)->inPort);
  393.         TEDeactivate(teHndl);
  394.         if (teHndl == gActiveTEHndl)
  395.             gActiveTEHndl = nil;
  396.         viewCtl = CTEViewFromTE(teHndl);
  397.         CTEUpdate(teHndl, viewCtl, true);
  398.         SetPort(oldPort);
  399.         return;
  400.     }
  401.  
  402.     if (!(viewCtl = CTEViewFromTE(teHndl))) return;
  403.  
  404.     teData = (CTEDataHndl)(*viewCtl)->contrlData;
  405.     (*teData)->mode |= cteActive;
  406.     GetPort(&oldPort);
  407.     SetPort(tePort = (*teHndl)->inPort);
  408.     TEActivate(gActiveTEHndl = teHndl);
  409.     CTEUpdate(teHndl, viewCtl, true);
  410.     SetPort(oldPort);
  411.         /* Let TextEdit know that it is supposed to be active. */
  412.  
  413.     for (viewCtl = nil;;) {
  414.         viewCtl = CTENext(tePort, &te, viewCtl);
  415.         if (!viewCtl) break;
  416.         if (te != teHndl) {
  417.             teData = (CTEDataHndl)(*viewCtl)->contrlData;
  418.             (*teData)->mode &= (0xFFFF - cteActive);
  419.             CTEActivate(false, te);
  420.         }
  421.     }
  422. }
  423.  
  424.  
  425.  
  426. static void    dummyCTEActivate(Boolean active, TEHandle teHndl)
  427. {
  428. #pragma unused (active, teHndl)
  429. }
  430.  
  431.  
  432.  
  433. /*****************************************************************************/
  434.  
  435.  
  436.  
  437. /* This is called when a mouseDown occurs in the content of a window.  It
  438. ** returns true if the mouseDown caused a TextEdit action to occur.  Events
  439. ** that are handled include if the user clicks on a scrollbar that is
  440. ** associated with a TextEdit control. */
  441.  
  442. #pragma segment Controls
  443. Boolean    CTEClick(WindowPtr window, EventRecord *event, short *action)
  444. {
  445.     WindowPtr        oldPort;
  446.     Point            mouseLoc;
  447.     TEHandle        te, teActive;
  448.     ControlHandle    ctlHit, viewCtl;
  449.     CTEDataHndl        teData;
  450.     Boolean            vert;
  451.     short            extendSelect, part, value, newValue, lh, mode;
  452.  
  453.     if (action)
  454.         *action = 0;
  455.  
  456.     GetPort(&oldPort);
  457.     if (!((WindowPeek)window)->hilited) return(false);
  458.  
  459.     SetPort(window);
  460.     mouseLoc = event->where;
  461.     GlobalToLocal(&mouseLoc);
  462.  
  463.     if (CTEFindCtl(window, event, &te, &ctlHit)) {
  464.             /* See if the user clicked directly on the view control for a
  465.             ** TextEdit record.  If so, we definitely have some work to do.
  466.             */
  467.         if (te != gActiveTEHndl) {
  468.             CTEActivate(true, te);
  469.             if (action)
  470.                 *action = -1;
  471.             SetPort(oldPort);
  472.             return(true);
  473.                 /* If user clicked on TextEdit control other than the
  474.                 ** currently active control, then activate it.  This is our
  475.                 ** only action in this case. */
  476.         }
  477.         extendSelect = ((event->modifiers & shiftKey) != 0);
  478.             /* Extend-select may be occuring. */
  479.  
  480.         gCanGoSlow = true;
  481.             /* There is a slow-zone around the TextEdit area for slow extend-select.
  482.             ** Allow this zone to slow down selection. */
  483.  
  484.         TEClick(mouseLoc, extendSelect, te);
  485.             /* Do the extend-select thing.  Most of the work is handled by
  486.             ** TextEdit.  The only thing we have to do is to update the
  487.             ** scrollbars while the user is extending the select.  This is
  488.             ** taken care of by our custom clikLoop procedure.
  489.             */
  490.  
  491.         if (viewCtl = CTEViewFromTE(te)) {
  492.             teData = (CTEDataHndl)(*viewCtl)->contrlData;
  493.             (*teData)->newUndo = true;
  494.         }
  495.  
  496.         SetPort(oldPort);
  497.         return(true);
  498.     }
  499.  
  500. /* We didn't hit the view control for a TextEdit record, but don't give up yet.
  501. ** The user may be clicking on a related scrollbar.  Let's find out... */
  502.  
  503.     if (WhichControl(mouseLoc, window, &ctlHit)) {
  504.             /* The user did click on a control.  But is it a scrollbar
  505.             ** for a TextEdit control?  Stay tuned... */
  506.  
  507.         te = CTEFromScroll(ctlHit, &viewCtl);
  508.  
  509.         if (te) {        /* It was a related scrollbar. */
  510.  
  511.             if (te != gActiveTEHndl) {
  512.                 CTEActivate(true, te);
  513.                 if (action)
  514.                     *action = -1;
  515.                 SetPort(oldPort);
  516.                 return(true);
  517.                     /* If user clicked on TextEdit control other than the
  518.                     ** currently active control, then activate it.  This is our
  519.                     ** only action in this case. */
  520.             }
  521.  
  522.             vert = ((*ctlHit)->contrlRect.top <= (*viewCtl)->contrlRect.top);
  523.                 /* Horizontal or vertical scroll.  Only the rect knows. */
  524.  
  525.             part = FindControl(mouseLoc, window, &ctlHit);
  526.  
  527.             switch (part) {
  528.                 case 0:        /* Inactive scrollbar. */
  529.                     break;
  530.                 case inThumb:
  531.                     value = GetCtlValue(ctlHit);
  532.                     part  = TrackControl(ctlHit, mouseLoc, nil);
  533.                     if (part) {
  534.                         newValue = GetCtlValue(ctlHit);
  535.                         if (value != newValue) {    /* If scrollbar value changed... */
  536.                             if (vert) {
  537.                                 teData = (CTEDataHndl)(*viewCtl)->contrlData;
  538.                                 mode   = (*teData)->mode;
  539.                                 if (mode & cteScrollFullLines) {
  540.                                     lh = (*te)->lineHeight;
  541.                                     newValue += (lh >> 1);
  542.                                     newValue /= lh;
  543.                                     newValue *= lh;
  544.                                     SetCtlValue(ctlHit, newValue);
  545.                                 }
  546.                                 TEScroll(0, value - newValue, te);
  547.                             }
  548.                             else
  549.                                 TEScroll(value - newValue, 0, te);
  550.                         }
  551.                     }
  552.                     break;
  553.  
  554.                 default:
  555.                     teActive = gActiveTEHndl;
  556.                     gActiveTEHndl = te;
  557.                         /* This is a hack way to pass the action procedure
  558.                         ** which TextEdit record we are dealing with.
  559.                         */
  560.                     if (vert)
  561.                         TrackControl(ctlHit, mouseLoc, (ProcPtr)VActionProc);
  562.                     else
  563.                         TrackControl(ctlHit, mouseLoc, (ProcPtr)HActionProc);
  564.  
  565.                     gActiveTEHndl = teActive;
  566.                         /* Unhack our previous hack. */
  567.                     break;
  568.             }
  569.  
  570.             SetPort(oldPort);
  571.             return(true);
  572.         }
  573.     }
  574.  
  575.     SetPort(oldPort);
  576.     return(false);
  577. }
  578.  
  579.  
  580.  
  581. static Boolean    dummyCTEClick(WindowPtr window, EventRecord *event, short *action)
  582. {
  583. #pragma unused (window, event)
  584.  
  585.     if (action)
  586.         *action = 0;
  587.     return(false);
  588. }
  589.  
  590.  
  591.  
  592. /*****************************************************************************/
  593.  
  594.  
  595.  
  596. /* This is the custom clikLoop, which is called from the assembly glue code.
  597. ** This handles updating the scrollbars as the user is drag-selecting in
  598. ** the TextEdit control. */
  599.  
  600. #pragma segment Controls
  601. void    CTEClikLoop(void)
  602. {
  603.     WindowPtr        oldPort, window;
  604.     TEHandle        te;
  605.     Point            mouseLoc;
  606.     Rect            viewRct;
  607.     RgnHandle        rgn;
  608.     short            dl, dr, dt, db, lh;
  609.     long            tick;
  610.  
  611.     GetPort(&oldPort);
  612.     SetPort(window = (WindowPtr)(*gActiveTEHndl)->inPort);
  613.  
  614.     te = gActiveTEHndl;
  615.         /* This better be what the user is dragging, or we did something
  616.         ** wrong elsewhere.
  617.         */
  618.  
  619.     GetMouse(&mouseLoc);
  620.     viewRct = (*te)->viewRect;
  621.  
  622.     if (!PtInRect(mouseLoc, &viewRct)) {
  623.         /* User is outside the TextEdit area, so scrolling is happening. */
  624.  
  625.         if (tick = gCanGoSlow)
  626.             tick = TickCount();
  627.                 /* As an extra feature, there is a zone around the TextEdit
  628.                 ** viewRect that the scroll will be slowed down.  This zone
  629.                 ** is based on the lineHeight of the active TextEdit control.
  630.                 ** If the user drags outside the viewRect further than the
  631.                 ** value of lineHeight, then scrolling occurs as fast as possible. */
  632.  
  633.         rgn = NewRgn();
  634.         GetClip(rgn);
  635.         ClipRect(&(window->portRect));
  636.             /* The clipRgn is set to protect everything outside viewRect.
  637.             ** This doesn't work very well when we want to change
  638.             ** the scrollbars.  Save the old and open it up.
  639.             */
  640.  
  641.         dl = viewRct.left - mouseLoc.h;
  642.         dr = mouseLoc.h   - viewRct.right;
  643.         dt = viewRct.top  - mouseLoc.v;
  644.         db = mouseLoc.v   - viewRct.bottom;
  645.             /* Check the delta value for each side of viewRect.  This will
  646.             ** be used to determine if we should scroll fast or slow.
  647.             */
  648.  
  649.         CTEAdjustScrollValues(te);
  650.             /* Scroll them puppies. */
  651.  
  652.         SetClip(rgn);                                /* restore clip */
  653.         DisposeRgn(rgn);
  654.             /* Make Mr. clipRgn happy again. */
  655.  
  656.         lh = (*te)->lineHeight;
  657.         if ((dl < lh) && (dr < lh) && (dt < lh) && (db < lh))
  658.             while (tick + 10 > TickCount());
  659.                 /* Do it really slow.  (This is important on very fast machines.) */
  660.     }
  661.  
  662.     SetPort(oldPort);
  663. }
  664.  
  665.  
  666.  
  667. /*****************************************************************************/
  668.  
  669.  
  670.  
  671. /* Do the cut/copy/paste/clear operations for the currently active
  672. ** TextEdit control.  Caller assumes appropriateness of the call.  Typically,
  673. ** this routine won't be called at an inappropriate time, since the menu
  674. ** item should be enabled or disabled correctly.
  675. ** Use CTEEditMenu to set the menu items undo/cut/copy/paste/clear correctly
  676. ** for the active TextEdit control.  Since undo isn't currently supported,
  677. ** all that CTEEditMenu does for the undo case is to deactivate it right now. */
  678.  
  679. #pragma segment Controls
  680. void    CTEClipboard(short menuID)
  681. {
  682.     WindowPtr        oldPort;
  683.     TEHandle        te;
  684.     ControlHandle    viewCtl;
  685.     short            maxTextLen, charsToAdd;
  686.  
  687.     if (!(te = gActiveTEHndl)) return;
  688.     if (!(viewCtl = CTEViewFromTE(te))) return;
  689.  
  690.     GetPort(&oldPort);
  691.     SetPort((*te)->inPort);
  692.     switch (menuID) {
  693.         case 2:
  694.             CTENewUndo(viewCtl, true);
  695.             TECut(te);
  696.             break;
  697.         case 3:
  698.             TECopy(te);
  699.             break;
  700.         case 4:
  701.             if (viewCtl) {
  702.                 maxTextLen = (*(CTEDataHndl)((*viewCtl)->contrlData))->maxTextLen;
  703.                 charsToAdd = TEGetScrapLen() - ((*te)->selEnd - (*te)->selStart);
  704.                 if ((*te)->teLength + charsToAdd <= maxTextLen) {
  705.                     CTENewUndo(viewCtl, true);
  706.                     TEPaste(te);
  707.                 }
  708.             }
  709.             break;
  710.         case 5:
  711.             CTENewUndo(viewCtl, true);
  712.             TEDelete(te);
  713.             break;
  714.     }
  715.  
  716.     CTEAdjustTEBottom(te);
  717.     CTEAdjustScrollValues(te);
  718.     SetPort(oldPort);
  719. }
  720.  
  721.  
  722.  
  723. static void    dummyCTEClipboard(short menuID)
  724. {
  725. #pragma unused (menuID)
  726. }
  727.  
  728.  
  729.  
  730. /*****************************************************************************/
  731.  
  732.  
  733.  
  734. #pragma segment Controls
  735. void    CTEConvertClipboard(Boolean convertClipboard, Boolean becomingActive)
  736. {
  737.     if (becomingActive) {
  738.         if (convertClipboard)
  739.             TEFromScrap();            /* Convert clipboard to private scrap. */
  740.     } else {
  741.         ZeroScrap();
  742.         if (TEToScrap())
  743.             ZeroScrap();            /* Convert private scrap to public. */
  744.     }
  745. }
  746.  
  747.  
  748.  
  749. static void    dummyCTEConvertClipboard(Boolean convertClipboard, Boolean becomingActive)
  750. {
  751. #pragma unused (convertClipboard, becomingActive)
  752. }
  753.  
  754.  
  755.  
  756. /*****************************************************************************/
  757.  
  758.  
  759.  
  760. #pragma segment Controls
  761. static pascal long    CTECtl(short varCode, ControlHandle ctl, short msg, long parm)
  762. {
  763. #pragma unused (varCode)
  764.  
  765.     Rect            viewRct;
  766.     TEHandle        te;
  767.     CTEDataHndl        teData;
  768.     ControlHandle    scrollCtl;
  769.     short            i;
  770.  
  771.     if (te = (TEHandle)GetCRefCon(ctl))
  772.         viewRct = (*ctl)->contrlRect;
  773.     else
  774.         SetRect(&viewRct, 0, 0, 0, 0);
  775.  
  776.     switch (msg) {
  777.         case drawCntl:
  778.             CTEUpdate(te, ctl, false);
  779.             break;
  780.  
  781.         case testCntl:
  782.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  783.                 gFoundViewCtl = ctl;
  784.                 gFoundTEHndl  = te;
  785.                 return(1);
  786.             }
  787.             return(0);
  788.             break;
  789.  
  790.         case calcCRgns:
  791.         case calcCntlRgn:
  792.             if (msg == calcCRgns)
  793.                 parm &= 0x00FFFFFF;
  794.             RectRgn((RgnHandle)parm, &viewRct);
  795.             break;
  796.  
  797.         case initCntl:
  798.             break;
  799.  
  800.         case dispCntl:
  801.             if (te) {
  802.                 if (te == gActiveTEHndl)
  803.                     gActiveTEHndl = nil;
  804.                 TEDispose(te);
  805.                 if (teData = (CTEDataHndl)(*ctl)->contrlData) {
  806.                     if ((*teData)->undoText)
  807.                         DisposeHandle((Handle)(*teData)->undoText);
  808.                     DisposeHandle((Handle)teData);
  809.                 }
  810.                 for (i = 0; i < 2; ++i)
  811.                     if (scrollCtl = CTEScrollFromView(ctl, i))
  812.                         DisposeControl(scrollCtl);
  813.             }
  814.             break;
  815.  
  816.         case posCntl:
  817.             break;
  818.  
  819.         case thumbCntl:
  820.             break;
  821.  
  822.         case dragCntl:
  823.             break;
  824.  
  825.         case autoTrack:
  826.             break;
  827.     }
  828.  
  829.     return(0);
  830. }
  831.  
  832.  
  833.  
  834. /*****************************************************************************/
  835.  
  836.  
  837.  
  838. /* The TextEdit control that was hit by calling FindControl is saved in a
  839. ** global variable, since the CDEF has no way of returning what kind it was.
  840. ** To determine that it was a TextEdit control that was hit, first call this
  841. ** function.  The first call returns the old value in the global variable,
  842. ** plus it resets the global to nil.  Then call FindControl(), and then
  843. ** call this function again.  If it returns nil, then a TextEdit control
  844. ** wasn't hit.  If it returns non-nil, then it was a TextEdit control that
  845. ** was hit, and specifically the one returned. */
  846.  
  847. #pragma segment Controls
  848. ControlHandle    CTECtlHit(void)
  849. {
  850.     ControlHandle    ctl;
  851.  
  852.     ctl = gFoundViewCtl;
  853.     gFoundViewCtl = nil;
  854.     return(ctl);
  855. }
  856.  
  857.  
  858.  
  859. static ControlHandle    dummyCTECtlHit(void)
  860. {
  861.     return(nil);
  862. }
  863.  
  864.  
  865.  
  866. /*****************************************************************************/
  867.  
  868.  
  869.  
  870. /* Disposes of the TERecord, TextEdit control, and any related scrollbars. */
  871.  
  872. #pragma segment Controls
  873. void    CTEDispose(TEHandle teHndl)
  874. {
  875.     WindowPtr    oldPort;
  876.  
  877.     if (teHndl) {
  878.         GetPort(&oldPort);
  879.         SetPort((*teHndl)->inPort);
  880.         TEDispose(CTEDisposeView(CTEViewFromTE(teHndl)));
  881.             /* Dispose of the TextEdit control completely.  This includes
  882.             ** scrollbars, as well as the TextEdit view control.
  883.             */
  884.         SetPort(oldPort);
  885.     }
  886. }
  887.  
  888.  
  889.  
  890. static void    dummyCTEDispose(TEHandle teHndl)
  891. {
  892. #pragma unused (teHndl)
  893. }
  894.  
  895.  
  896.  
  897. /*****************************************************************************/
  898.  
  899.  
  900.  
  901. /* Dispose of the view control and related scrollbars.  This function also
  902. ** returns the handle to the TextEdit record, since it was just orphaned.
  903. ** Use this function if you want to get rid of a TextEdit control, but you
  904. ** want to keep the TextEdit record. */
  905.  
  906. #pragma segment Controls
  907. TEHandle    CTEDisposeView(ControlHandle viewCtl)
  908. {
  909.     TEHandle        te;
  910.     short            vert;
  911.     ControlHandle    ctl;
  912.  
  913.     if (!viewCtl) return(nil);
  914.  
  915.     te = (TEHandle)GetCRefCon(viewCtl);
  916.     SetCRefCon(viewCtl, (long)nil);
  917.  
  918.     for (vert = 0; vert < 2; ++vert)
  919.         if (ctl = CTEScrollFromView(viewCtl, vert))
  920.             DisposeControl(ctl);
  921.  
  922.     DisposeControl(viewCtl);
  923.     return(te);
  924. }
  925.  
  926.  
  927.  
  928. static TEHandle    dummyCTEDisposeView(ControlHandle viewCtl)
  929.  
  930. {
  931. #pragma unused (viewCtl)
  932.     return(nil);
  933. }
  934.  
  935.  
  936.  
  937. /*****************************************************************************/
  938.  
  939.  
  940.  
  941. /* Returns the full document height. */
  942.  
  943. #pragma segment Controls
  944. short    CTEDocHeight(TEHandle teHndl)
  945. {
  946.     if (!teHndl) return(0);
  947.  
  948.     return(CTENumTextLines(teHndl) * (*teHndl)->lineHeight);
  949. }
  950.  
  951.  
  952.  
  953. static short    dummyCTEDocHeight(TEHandle teHndl)
  954. {
  955. #pragma unused (teHndl)
  956.     return(0);
  957. }
  958.  
  959.  
  960.  
  961. /*****************************************************************************/
  962.  
  963.  
  964.  
  965. /* Enable or disable edit menu items based on the active TextEdit control.
  966. ** You pass the menu ID of the undo item in undoID, and the menu ID of the
  967. ** cut item in cutID.  If undoID or cutID is non-zero, then some action is
  968. ** performed.  If you pass a non-zero value for cutID, then the other menu
  969. ** items cut/copy/paste/clear are updated to reflect the status of the
  970. ** active TextEdit control. */
  971.  
  972. #pragma segment Controls
  973. Boolean    CTEEditMenu(Boolean *activeItem, short editMenu, short undoID, short cutID)
  974. {
  975.     TEHandle        te;
  976.     MenuHandle        menu;
  977.     Boolean            active;
  978.     ControlHandle    viewCtl;
  979.     CTEDataHndl        teData;
  980.  
  981.     *activeItem = active = false;
  982.     menu = GetMHandle(editMenu);
  983.  
  984.     if (undoID)
  985.         DisableItem(menu, undoID);
  986.  
  987.     if (cutID) {
  988.         DisableItem(menu, cutID);            /* Disable cut. */
  989.         DisableItem(menu, cutID + 1);        /* Disable copy. */
  990.         DisableItem(menu, cutID + 2);        /* Disable paste. */
  991.         DisableItem(menu, cutID + 3);        /* Disable clear. */
  992.     }
  993.  
  994.     if (!(te = gActiveTEHndl)) return(false);
  995.  
  996.     if (undoID) {
  997.         if (viewCtl = CTEViewFromTE(te)) {
  998.             teData = (CTEDataHndl)(*viewCtl)->contrlData;
  999.             if ((*teData)->undoText) {
  1000.                 EnableItem(menu, undoID);
  1001.                 active = true;
  1002.             }
  1003.         }
  1004.     }
  1005.  
  1006.     if (cutID) {
  1007.         if ((*te)->selStart != (*te)->selEnd) {
  1008.             if (!CTEReadOnly(te)) {
  1009.                 EnableItem(menu, cutID);        /* Enable cut. */
  1010.                 EnableItem(menu, cutID + 3);    /* Enable clear. */
  1011.             }
  1012.             active = true;
  1013.             EnableItem(menu, cutID + 1);        /* Enable copy. */
  1014.         }
  1015.         if (!CTEReadOnly(te)) {
  1016.             if (TEGetScrapLen()) {
  1017.                 active = true;
  1018.                 EnableItem(menu, cutID + 2);        /* Enable paste. */
  1019.             }
  1020.         }
  1021.     }
  1022.  
  1023.     *activeItem = active;
  1024.     return(true);
  1025. }
  1026.  
  1027.  
  1028.  
  1029. static Boolean    dummyCTEEditMenu(Boolean *activeItem, short editMenu, short undoID, short cutID)
  1030. {
  1031. #pragma unused (editMenu, undoID, cutID)
  1032.     *activeItem = false;
  1033.     return(false);
  1034. }
  1035.  
  1036.  
  1037.  
  1038. /*****************************************************************************/
  1039.  
  1040.  
  1041.  
  1042. /* Handle the event if it applies to the active TextEdit control.  If some
  1043. ** action occured due to the event, return true. */
  1044.  
  1045. #pragma segment Controls
  1046. Boolean    CTEEvent(WindowPtr window, EventRecord *event, short *action)
  1047. {
  1048.     WindowPtr    clickWindow;
  1049.     short        actn;
  1050.  
  1051.     if (action)
  1052.         *action = 0;
  1053.  
  1054.     switch(event->what) {
  1055.  
  1056.         case mouseDown:
  1057.             if (FindWindow(event->where, &clickWindow) == inContent)
  1058.                 if (window == clickWindow)
  1059.                     if (((WindowPeek)window)->hilited) return(CTEClick(window, event, action));
  1060.             break;
  1061.  
  1062.         case autoKey:
  1063.         case keyDown:
  1064.             if (!(event->modifiers & cmdKey)) {
  1065.                 actn = CTEKey(window, event);
  1066.                 if (action)
  1067.                     *action = actn;
  1068.                 if (actn) return(true);
  1069.             }
  1070.             break;
  1071.     }
  1072.  
  1073.     return(false);
  1074. }
  1075.  
  1076.  
  1077.  
  1078. static Boolean    dummyCTEEvent(WindowPtr window, EventRecord *event, short *action)
  1079. {
  1080. #pragma unused (window, event)
  1081.  
  1082.     if (action)
  1083.         *action = 0;
  1084.     return(false);
  1085. }
  1086.  
  1087.  
  1088.  
  1089. /*****************************************************************************/
  1090.  
  1091.  
  1092.  
  1093. /* Select or reselect a range of text without flashing or lurching. */
  1094.  
  1095. #pragma segment Controls
  1096. void    CTEFakeClick(short newStart, short newEnd, Boolean extend, TEHandle teHndl)
  1097. {
  1098.     WindowPtr    oldPort;
  1099.     short        lineHeight, oldStart, oldEnd, newLoc, dx, dy, selTop, selBot;
  1100.     Point        fakeClick;
  1101.     Rect        viewRct;
  1102.  
  1103.     if (!teHndl) return;
  1104.  
  1105.     if (newStart < 0)
  1106.         newStart = 0;
  1107.     if (newEnd > (*teHndl)->teLength)
  1108.         newEnd = (*teHndl)->teLength;
  1109.  
  1110.     oldStart = (*teHndl)->selStart;
  1111.     oldEnd   = (*teHndl)->selEnd;
  1112.     if ((extend) && (oldStart == newStart) && (oldEnd == newEnd)) return;
  1113.  
  1114.     GetPort(&oldPort);
  1115.     SetPort((*teHndl)->inPort);
  1116.     TEAutoView(false, teHndl);
  1117.     lineHeight = (*teHndl)->lineHeight;
  1118.  
  1119.     newLoc = (newStart < oldStart) ? newStart : newEnd;
  1120.  
  1121.     fakeClick = TEGetPoint(newLoc, teHndl);
  1122.     fakeClick.v -= (lineHeight >> 1);
  1123.     if (newLoc != TEGetOffset(fakeClick, teHndl))
  1124.         fakeClick.v += lineHeight;
  1125.  
  1126.     (*teHndl)->clickTime = 0L;
  1127.         /* Keystrokes for extend-select shouldn't be able to dbl-click. */
  1128.  
  1129.     gCanGoSlow = false;
  1130.         /* Since we are using TEClick to handle the hilight, make sure that
  1131.         ** our slow-select feature doesn't work for keys. */
  1132.  
  1133.     TEClick(fakeClick, extend, teHndl);
  1134.         /* Do new selection without flash like extend-select with mouse does it. */
  1135.  
  1136.     (*teHndl)->clickTime = 0L;
  1137.         /* We just did a key, not a click, so reset the click timer. */
  1138.  
  1139.     selBot = (fakeClick.v += (lineHeight >> 1));
  1140.     selTop = (fakeClick.v -= lineHeight);
  1141.  
  1142.     viewRct = (*teHndl)->viewRect;
  1143.     dy = 0;
  1144.     if (selTop < viewRct.top)
  1145.         dy = selTop - viewRct.top;
  1146.     if (selBot > viewRct.bottom)
  1147.         dy = selBot - viewRct.bottom;
  1148.  
  1149.     --fakeClick.h;
  1150.     dx = 0;
  1151.     if (fakeClick.h < viewRct.left)
  1152.         dx = fakeClick.h - viewRct.left;
  1153.     if (fakeClick.h > viewRct.right)
  1154.         dx = fakeClick.h - viewRct.right + 1;
  1155.  
  1156.     if (dx | dy)
  1157.         TEScroll(-dx, -dy, teHndl);
  1158.  
  1159.     TEAutoView(true, teHndl);
  1160.     (*teHndl)->clikLoop = ASMTECLIKLOOP;
  1161.     CTEAdjustTEBottom(teHndl);
  1162.     CTEAdjustScrollValues(teHndl);
  1163.     SetPort(oldPort);
  1164. }
  1165.  
  1166.  
  1167.  
  1168. static void    dummyCTEFakeClick(short newStart, short newEnd, Boolean extend, TEHandle teHndl)
  1169. {
  1170. #pragma unused (newStart, newEnd, extend, teHndl)
  1171. }
  1172.  
  1173.  
  1174.  
  1175. /*****************************************************************************/
  1176.  
  1177.  
  1178.  
  1179. /* Returns the active TextEdit control, if any.  If nil is passed in, then
  1180. ** the return value represents whatever TextEdit control is active, independent
  1181. ** of what window it is in.  If a window is passed in, then it returns a
  1182. ** TextEdit control only if the active control is in the specified window.
  1183. ** If the active TextEdit control is in some other window, then nil is returned. */
  1184.  
  1185. #pragma segment Controls
  1186. TEHandle    CTEFindActive(WindowPtr window)
  1187. {
  1188.     if (!window) return(gActiveTEHndl);
  1189.         /* User wants whatever is active one, for whatever window. */
  1190.  
  1191.     if (!gActiveTEHndl) return(nil);
  1192.     if (window != (*gActiveTEHndl)->inPort) return(nil);
  1193.     return(gActiveTEHndl);
  1194. }
  1195.  
  1196.  
  1197.  
  1198. static TEHandle    dummyCTEFindActive(WindowPtr window)
  1199. {
  1200. #pragma unused (window)
  1201.     return(nil);
  1202. }
  1203.  
  1204.  
  1205.  
  1206. /*****************************************************************************/
  1207.  
  1208.  
  1209.  
  1210. /* This determines if a TextEdit control was clicked on directly.  This does
  1211. ** not determine if a related scrollbar was clicked on.  If a TextEdit
  1212. ** control was clicked on, then true is returned, as well as the TextEdit
  1213. ** handle and the handle to the view control. */
  1214.  
  1215. #pragma segment Controls
  1216. Boolean    CTEFindCtl(WindowPtr window, EventRecord *event, TEHandle *teHndl, ControlHandle *ctlHit)
  1217. {
  1218.     WindowPtr        oldPort;
  1219.     Point            mouseLoc;
  1220.  
  1221.     *teHndl = nil;
  1222.  
  1223.     if (window) {
  1224.         GetPort(&oldPort);
  1225.         SetPort(window);
  1226.         mouseLoc = event->where;
  1227.         GlobalToLocal(&mouseLoc);
  1228.         SetPort(oldPort);
  1229.  
  1230.         gFoundTEHndl = nil;
  1231.         FindControl(mouseLoc, window, ctlHit);
  1232.         if (*teHndl = gFoundTEHndl) return(true);
  1233.     }
  1234.  
  1235.     *ctlHit = nil;
  1236.     return(false);
  1237. }
  1238.  
  1239.  
  1240.  
  1241. static Boolean    dummyCTEFindCtl(WindowPtr window, EventRecord *event, TEHandle *teHndl, ControlHandle *ctlHit)
  1242. {
  1243. #pragma unused (window, event)
  1244.     *teHndl = nil;
  1245.     *ctlHit = nil;
  1246.     return(false);
  1247. }
  1248.  
  1249.  
  1250.  
  1251. /*****************************************************************************/
  1252.  
  1253.  
  1254.  
  1255. /* Find the TextEdit record that is related to the indicated scrollbar. */
  1256.  
  1257. #pragma segment Controls
  1258. TEHandle    CTEFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  1259. {
  1260.     WindowPtr        window;
  1261.     ControlHandle    viewCtl;
  1262.     TEHandle        te;
  1263.  
  1264.     if (!IsScrollBar(scrollCtl)) {
  1265.         *retCtl = nil;
  1266.         return(nil);
  1267.     }
  1268.  
  1269.     window = (*scrollCtl)->contrlOwner;
  1270.  
  1271.     for (*retCtl = viewCtl = nil;;) {
  1272.         viewCtl = CTENext(window, &te, viewCtl);
  1273.         if (!viewCtl) return(nil);
  1274.         if (viewCtl == (ControlHandle)GetCRefCon(scrollCtl)) {
  1275.             *retCtl = viewCtl;
  1276.             return(te);
  1277.         }
  1278.     }
  1279. }
  1280.  
  1281.  
  1282.  
  1283. static TEHandle    dummyCTEFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  1284. {
  1285. #pragma unused (scrollCtl)
  1286.     *retCtl = nil;
  1287.     return(nil);
  1288. }
  1289.  
  1290.  
  1291.  
  1292. /*****************************************************************************/
  1293.  
  1294.  
  1295.  
  1296. /* Hide the designated TextEdit control and related scrollbars. */
  1297.  
  1298. #pragma segment Controls
  1299. void    CTEHide(TEHandle teHndl)
  1300. {
  1301.     ControlHandle    viewCtl, scrollCtl;
  1302.     short            i;
  1303.  
  1304.     if (teHndl) {
  1305.         CTEActivate(false, teHndl);
  1306.         viewCtl = CTEViewFromTE(teHndl);
  1307.         if (viewCtl) {
  1308.             HideControl(viewCtl);
  1309.             for (i = 0; i < 2; i++) {
  1310.                 scrollCtl = CTEScrollFromView(viewCtl, i);
  1311.                 if (scrollCtl)
  1312.                     HideControl(scrollCtl);
  1313.             }
  1314.         }
  1315.     }
  1316. }
  1317.  
  1318.  
  1319.  
  1320. static void    dummyCTEHide(TEHandle teHndl)
  1321. {
  1322. #pragma unused (teHndl)
  1323. }
  1324.  
  1325.  
  1326.  
  1327. /*****************************************************************************/
  1328.  
  1329.  
  1330.  
  1331. /* Blink the caret in the active TextEdit control.  The active TextEdit
  1332. ** control may be read-only, in which case the caret does not blink. */
  1333.  
  1334. #pragma segment Controls
  1335. void    CTEIdle(void)
  1336. {
  1337.     WindowPtr    window;
  1338.  
  1339.     if (gActiveTEHndl) {
  1340.         window = (*gActiveTEHndl)->inPort;
  1341.         if (((WindowPeek)window)->hilited)
  1342.             if (GetWRefCon(window))
  1343.                 TEIdle(gActiveTEHndl);
  1344.     }
  1345. }
  1346.  
  1347.  
  1348.  
  1349. static void    dummyCTEIdle(void)
  1350. {
  1351. }
  1352.  
  1353.  
  1354.  
  1355. /*****************************************************************************/
  1356.  
  1357.  
  1358.  
  1359. /* See if the keypress event applies to the TextEdit control, and if it does,
  1360. ** handle it and return non-zero.  If the key caused a change in the TERecord,
  1361. ** return 2.  If the key was handled with no change to the TERecord, return 1. */
  1362.  
  1363. #pragma segment Controls
  1364. short    CTEKey(WindowPtr window, EventRecord *event)
  1365. {
  1366.     TEHandle            te;
  1367.     ControlHandle        viewCtl;
  1368.     short                maxTextLen, selStart, selEnd;
  1369.     short                textSelected, arrowKey, lineHeight, retval;
  1370.     char                key;
  1371.     CTEDataHndl            teData;
  1372.     Point                pt;
  1373.     CTEKeyFilterProcPtr    proc;
  1374.     Boolean                handled;
  1375.  
  1376.     if (!(te = gActiveTEHndl))                return(0);
  1377.     if ((*gActiveTEHndl)->inPort != window) return(0);
  1378.     if (CTEReadOnly(te))                    return(0);
  1379.     if (!(viewCtl = CTEViewFromTE(te)))        return(0);
  1380.  
  1381.     teData = (CTEDataHndl)(*viewCtl)->contrlData;
  1382.     if (proc = (*teData)->keyFilter)
  1383.         if ((*proc)(te, event, &handled))
  1384.             return(handled);
  1385.  
  1386.     maxTextLen = (*teData)->maxTextLen;
  1387.     key        = event->message & charCodeMask;
  1388.     selStart   = (*te)->selStart;
  1389.     selEnd     = (*te)->selEnd;
  1390.     if (selStart > selEnd) {
  1391.         selStart = selEnd;
  1392.         selEnd = (*te)->selStart;
  1393.     }
  1394.     textSelected = (selStart != selEnd);
  1395.     arrowKey     = ((key >= chLeft) && (key <= chDown));
  1396.     lineHeight   = (*te)->lineHeight;
  1397.  
  1398.     retval = 1;
  1399.     if (
  1400.         (textSelected) ||                /* If selection range to be replaced or */
  1401.         (arrowKey) ||                    /* key is an arrow or                    */
  1402.         (key == 8) ||                    /* key is a delete or                   */
  1403.         ((*te)->teLength < maxTextLen)    /* we have space for the key...         */
  1404.     ) {
  1405.         CTENewUndo(viewCtl, false);        /* Add the character. */
  1406.         if (arrowKey) {
  1407.             if (event->modifiers & shiftKey) {
  1408.                 switch (key) {
  1409.                     case chUp:
  1410.                         pt = TEGetPoint(selStart, te);
  1411.                         pt.v -= (lineHeight >> 1);
  1412.                         if (selStart == TEGetOffset(pt, te))
  1413.                             pt.v -= lineHeight;
  1414.                         selStart = TEGetOffset(pt, te);
  1415.                         CTEFakeClick(selStart, selEnd, true, te);
  1416.                         break;
  1417.                     case chDown:
  1418.                         pt = TEGetPoint(selEnd, te);
  1419.                         selEnd = TEGetOffset(pt, te);
  1420.                         CTEFakeClick(selStart, selEnd, true, te);
  1421.                         break;
  1422.                     case chLeft:
  1423.                         CTEFakeClick(--selStart, selEnd, true, te);
  1424.                         break;
  1425.                     case chRight:
  1426.                         CTEFakeClick(selStart, ++selEnd, true, te);
  1427.                         break;
  1428.                 }
  1429.                 CTEAdjustTEBottom(te);
  1430.                 CTEAdjustScrollValues(te);
  1431.                 return(retval);
  1432.             }
  1433.             if (textSelected) {
  1434.                 switch (key) {
  1435.                     case chUp:
  1436.                     case chLeft:
  1437.                         TESetSelect(selStart, selStart, te);
  1438.                         if (key == chUp)
  1439.                             textSelected = false;
  1440.                         break;
  1441.                     case chRight:
  1442.                     case chDown:
  1443.                         TESetSelect(selEnd, selEnd, te);
  1444.                         if (key == chDown)
  1445.                             textSelected = false;
  1446.                         break;
  1447.                 }
  1448.                 if (textSelected) {
  1449.                     CTEAdjustTEBottom(te);
  1450.                     CTEAdjustScrollValues(te);
  1451.                     return(retval);
  1452.                 }
  1453.             }
  1454.         }
  1455.  
  1456.         switch (key) {
  1457.             case chUp:
  1458.                 pt = TEGetPoint(selStart, te);
  1459.                 pt.v -= (lineHeight >> 1);
  1460.                 if (selStart == TEGetOffset(pt, te))
  1461.                     pt.v -= lineHeight;
  1462.                 selStart = TEGetOffset(pt, te);
  1463.                 CTEFakeClick(selStart, selStart, false, te);
  1464.                 break;
  1465.             case chLeft:
  1466.                 CTEFakeClick(--selStart, selEnd, false, te);
  1467.                 break;
  1468.             case chRight:
  1469.                 CTEFakeClick(selStart, ++selEnd, false, te);
  1470.                 break;
  1471.             default:
  1472.                 TEKey(key, te);
  1473.                 if (key != chDown)
  1474.                     ++retval;
  1475.                 break;
  1476.         }
  1477.  
  1478.         CTEAdjustTEBottom(te);
  1479.         CTEAdjustScrollValues(te);
  1480.     }
  1481.  
  1482.     return(retval);
  1483. }
  1484.  
  1485.  
  1486.  
  1487. static short    dummyCTEKey(WindowPtr window, EventRecord *event)
  1488. {
  1489. #pragma unused (window, event)
  1490.     return(0);
  1491. }
  1492.  
  1493.  
  1494.  
  1495. /*****************************************************************************/
  1496.  
  1497.  
  1498.  
  1499. /* This function is used to move a TextEdit control.  Pass it the TextEdit
  1500. ** record to move, plus the new position.  It will move the TextEdit control,
  1501. ** along with any scrollbars the control may have.  All areas that need
  1502. ** updating are cleared and invalidated. */
  1503.  
  1504. #pragma segment Controls
  1505. void    CTEMove(TEHandle teHndl, short newH, short newV)
  1506. {
  1507.     WindowPtr        oldPort;
  1508.     Rect            viewRct, brdrRct, rct;
  1509.     short            i, dx, dy, mode;
  1510.     ControlHandle    viewCtl, ctl;
  1511.     CTEDataHndl        teData;
  1512.  
  1513.     if (!(viewCtl = CTEViewFromTE(teHndl))) return;
  1514.  
  1515.     GetPort(&oldPort);
  1516.     SetPort((*teHndl)->inPort);
  1517.     teData = (CTEDataHndl)(*viewCtl)->contrlData;
  1518.  
  1519.     viewRct = (*viewCtl)->contrlRect;
  1520.     EraseRect(&viewRct);
  1521.     InvalRect(&viewRct);
  1522.     brdrRct = (*teData)->brdrRect;
  1523.     EraseRect(&brdrRct);
  1524.     InvalRect(&brdrRct);
  1525.  
  1526.     dx = newH - viewRct.left;
  1527.     dy = newV - viewRct.top;
  1528.  
  1529.     for (i = 0; i < 2; ++i) {
  1530.         if (ctl = CTEScrollFromView(viewCtl, i)) {
  1531.             rct = (*ctl)->contrlRect;
  1532.             MoveControl(ctl, rct.left + dx, rct.top + dy);
  1533.         }
  1534.     }
  1535.  
  1536.     OffsetRect(&viewRct, dx, dy);
  1537.     InvalRect(&viewRct);
  1538.     OffsetRect(&brdrRct, dx, dy);
  1539.     InvalRect(&brdrRct);
  1540.  
  1541.     (*viewCtl)->contrlRect = viewRct;
  1542.     (*teData)->brdrRect    = brdrRct;
  1543.  
  1544.     OffsetRect(&(*teHndl)->destRect, dx, dy);
  1545.     OffsetRect(&(*teHndl)->viewRect, dx, dy);
  1546.  
  1547.     mode = (*teData)->mode;
  1548.     viewRct.top  = viewRct.bottom - 16;
  1549.     viewRct.left = viewRct.right  - 16;
  1550.  
  1551.     if (mode & (cteVScrollLessGrow - cteVScroll + cteHScrollLessGrow - cteHScroll))
  1552.         EraseRect(&viewRct);
  1553.  
  1554.     if (mode & (cteVScrollLessGrow - cteVScroll))
  1555.         OffsetRect(&viewRct, 16, 0);
  1556.     if (mode & (cteHScrollLessGrow - cteHScroll))
  1557.         OffsetRect(&viewRct, 0, 16);
  1558.     InvalRect(&viewRct);
  1559.  
  1560.     SetPort(oldPort);
  1561. }
  1562.  
  1563.  
  1564.  
  1565. static void    dummyCTEMove(TEHandle teHndl, short newH, short newV)
  1566. {
  1567. #pragma unused (teHndl, newH, newV)
  1568. }
  1569.  
  1570.  
  1571.  
  1572. /*****************************************************************************/
  1573.  
  1574.  
  1575.  
  1576. /* Create a new TextEdit control.  See the comments at the beginning of this
  1577. ** file for more information.  Note that this function doesn't get a dummy,
  1578. ** as you really mean to have this code if you call it.  It calls CTEInitialize(),
  1579. ** just to make sure that the proc pointers are set to point to the real function,
  1580. ** instead of the dummy functions.  By having CLTENew() call CTEInitialize(), the
  1581. ** application won't have to worry about calling CTEInitialize().  By the time that
  1582. ** the application is using a TextEdit control, the proc pointers will be initialized. */
  1583.  
  1584. #pragma segment Controls
  1585. OSErr    CTENew(short viewID, WindowPtr window, TEHandle *teHndl, Rect *cRect, Rect *dRect,
  1586.                Rect *vRect, Rect *bRect, short maxTextLen, short mode)
  1587. {
  1588.     Rect            destRect, viewRect, brdrRect;
  1589.     WindowPtr        oldPort;
  1590.     TEHandle        te;
  1591.     short            width, height;
  1592.     Rect            ctlRect;
  1593.     ControlHandle    viewCtl, hScrollCtl, vScrollCtl;
  1594.     cdefRsrcJMPHndl    cdefRsrc;
  1595.     CTEDataHndl        teData;
  1596.     OSErr            err;
  1597.  
  1598.     CTEInitialize();
  1599.  
  1600.     *teHndl = nil;            /* Assume that we will fail. */
  1601.  
  1602.     gViewID = viewID;        /* Keep viewID that was passed in. */
  1603.  
  1604.     GetPort(&oldPort);
  1605.     SetPort(window);
  1606.  
  1607.     destRect = *dRect;
  1608.     viewRect = *vRect;
  1609.     brdrRect = *bRect;
  1610.         /* Make sure that the rects are not in memory that may move. */
  1611.  
  1612.     te = TENew(&destRect, &viewRect);
  1613.         /* Do the main thing. */
  1614.  
  1615.     err = noErr;
  1616.     viewCtl = hScrollCtl = vScrollCtl = nil;
  1617.         /* Prepare for various failures. */
  1618.  
  1619.     if (te) {        /* If we were able to create the TextEdit record... */
  1620.  
  1621.         TEAutoView(true, te);
  1622.             /* Let TextEdit 3.0 do most of the scrolling work. */
  1623.         gDefaultClikLoop = (*te)->clikLoop;
  1624.         (*te)->clikLoop  = ASMTECLIKLOOP;
  1625.             /* We will do the remainder of the work. */
  1626.  
  1627.         cdefRsrc = (cdefRsrcJMPHndl)GetResource('CDEF', viewID);
  1628.         if (!cdefRsrc) return(resNotFound);
  1629.  
  1630.         (*cdefRsrc)->jmpAddress = (long)CTECtl;
  1631.         FlushInstructionCache();
  1632.             /* Make sure that instruction caches don't kill us. */
  1633.  
  1634.         viewCtl = NewControl(window, cRect, nil, false, 0, 0, 0,
  1635.                              viewID * 16, (long)te);
  1636.             /* Use our custom view cdef.  It's wierd, but it's small. */
  1637.  
  1638.         if (!viewCtl)
  1639.             err = resNotFound;
  1640.         else {
  1641.             (*viewCtl)->contrlData = nil;
  1642.             if (teData = (CTEDataHndl)NewHandle(sizeof(CTEDataRec))) {
  1643.                 (*teData)->maxTextLen  = maxTextLen;
  1644.                 (*teData)->undoText    = nil;
  1645.                 (*teData)->mode        = mode;
  1646.                 (*teData)->brdrRect    = brdrRect;
  1647.                 (*teData)->brdrRect    = brdrRect;
  1648.                 (*teData)->keyFilter   = nil;
  1649.                 (*viewCtl)->contrlData = (Handle)teData;
  1650.             }
  1651.             else err = memFullErr;
  1652.  
  1653.             if (mode & cteHScroll) {        /* Caller wants a horizontal scrollbar... */
  1654.                 SetRect(&ctlRect, 0, 0, 100, 16);
  1655.                 hScrollCtl = NewControl(window, &ctlRect, nil, true, 0, 0, 0,
  1656.                                         scrollBarProc, (long)viewCtl);
  1657.                 if (hScrollCtl) {
  1658.                     MoveControl(hScrollCtl, brdrRect.left, brdrRect.bottom - 1);
  1659.                     width = brdrRect.right - brdrRect.left;
  1660.                     if (mode & (cteHScrollLessGrow - cteHScroll))
  1661.                         if (!(mode & cteVScroll))
  1662.                             width -= 15;
  1663.                     SizeControl(hScrollCtl, width, 16);
  1664.                         /* Line the scrollbar up with the borderRect. */
  1665.                 }
  1666.                 else err = resNotFound;
  1667.             }
  1668.  
  1669.             if (mode & cteVScroll) {        /* Caller wants a vertical scrollbar... */
  1670.                 SetRect(&ctlRect, 0, 0, 16, 100);
  1671.                 vScrollCtl = NewControl(window, &ctlRect, nil, true, 0, 0, 0,
  1672.                                         scrollBarProc, (long)viewCtl);
  1673.                 if (vScrollCtl) {
  1674.                     MoveControl(vScrollCtl, brdrRect.right - 1, brdrRect.top);
  1675.                     height = brdrRect.bottom - brdrRect.top;
  1676.                     if (mode & (cteVScrollLessGrow - cteVScroll))
  1677.                         if (!(mode & cteHScroll))
  1678.                             height -= 15;
  1679.                     SizeControl(vScrollCtl, 16, height);
  1680.                         /* Line the scrollbar up with the borderRect. */
  1681.                 }
  1682.                 else err = resNotFound;
  1683.             }
  1684.         }
  1685.     }
  1686.     else err = memFullErr;
  1687.  
  1688.     SetPort(oldPort);
  1689.  
  1690.     if (err) {        /* Oops.  Somebody wasn't happy. */
  1691.         if (viewCtl)
  1692.             DisposeControl(viewCtl);
  1693.                 /* This also disposes of TextEdit handle! */
  1694.         else
  1695.             if (te)
  1696.                 TEDispose(te);
  1697.                     /* We have to dispose of the TextEdit handle ourselves if
  1698.                     ** creating the view control failed. */
  1699.  
  1700.         te = nil;        /* Indicate that there is no TextEdit control. */
  1701.  
  1702.         if (hScrollCtl)
  1703.             DisposeControl(hScrollCtl);
  1704.                 /* More clean-up. */
  1705.  
  1706.         if (vScrollCtl)
  1707.             DisposeControl(vScrollCtl);
  1708.                 /* And still more clean-up. */
  1709.     }
  1710.     else {
  1711.         ShowControl(viewCtl);        /* Since everything worked, show the control. */
  1712.  
  1713.         if (mode & cteReadOnly)
  1714.             (*te)->caretHook = (ProcPtr)ASMNOCARET;
  1715.                 /* If read-only, then disable caret for this
  1716.                 ** TextEdit control. */
  1717.  
  1718.         if (mode & cteActive)
  1719.             CTEActivate(true, te);
  1720.  
  1721.         CTEAdjustScrollValues(*teHndl = te);
  1722.             /* Give the scrollbars an initial value.  This is because the
  1723.             ** TextEdit control could have been created with
  1724.             ** destRect.top < viewRect.top or destRect.left < viewRect.left.
  1725.             ** I don't know why anyone would want to, but it is legal.
  1726.             */
  1727.     }
  1728.  
  1729.     return(err);
  1730. }
  1731.  
  1732.  
  1733.  
  1734. /*****************************************************************************/
  1735.  
  1736.  
  1737.  
  1738. /* Save the data (if appropriate) so that user can undo. */
  1739.  
  1740. #pragma segment Controls
  1741. void    CTENewUndo(ControlHandle viewCtl, Boolean alwaysNewUndo)
  1742. {
  1743.     TEHandle    teHndl;
  1744.     CTEDataHndl    teData;
  1745.     Handle        hText, uText;
  1746.  
  1747.     if (!viewCtl) return;
  1748.  
  1749.     teHndl = (TEHandle)GetCRefCon(viewCtl);
  1750.     teData = (CTEDataHndl)(*viewCtl)->contrlData;
  1751.  
  1752.     hText = (*teHndl)->hText;
  1753.     uText = (*teData)->undoText;
  1754.         /* hText is text in the TextEdit record that is being edited.  */
  1755.         /* uText is the undo data (if any) prior to current edit task. */
  1756.  
  1757.     if (!uText) {                /* No undo text (therefore no editing) yet. */
  1758.         uText = NewHandle((*teHndl)->teLength);
  1759.         if (!uText) return;        /* Not enough memory to support undo. */
  1760.         (*teData)->undoText = uText;
  1761.         alwaysNewUndo = true;
  1762.             /* Since this is our first edit, we will want to cache the
  1763.             ** data, no matter for what reason we were called. */
  1764.     }
  1765.  
  1766.     if ((alwaysNewUndo) || ((*teData)->newUndo)) {
  1767.         SetHandleSize(uText, (*teHndl)->teLength);
  1768.         if (MemError()) return;
  1769.             /* Not enough memory to handle this undo. */
  1770.         BlockMove(*hText, *uText, (*teHndl)->teLength);
  1771.         (*teData)->newUndo      = false;
  1772.         (*teData)->undoSelStart = (*teHndl)->selStart;
  1773.         (*teData)->undoSelEnd   = (*teHndl)->selEnd;
  1774.     }
  1775. }
  1776.  
  1777.  
  1778.  
  1779. static void    dummyCTENewUndo(ControlHandle viewCtl, Boolean alwaysNewUndo)
  1780. {
  1781. #pragma unused (viewCtl, alwaysNewUndo)
  1782. }
  1783.  
  1784.  
  1785.  
  1786. /*****************************************************************************/
  1787.  
  1788.  
  1789.  
  1790. /* Get the next TextEdit control in the window.  You pass it a control handle
  1791. ** for the view control, or nil to start at the beginning of the window.
  1792. ** It returns both a TextEdit handle and the view control handle for that
  1793. ** TextEdit record.  If none is found, nil is returned.  This allows you to
  1794. ** repeatedly call this function and walk through all the TextEdit controls
  1795. ** in a window. */
  1796.  
  1797. #pragma segment Controls
  1798. ControlHandle    CTENext(WindowPtr window, TEHandle *teHndl, ControlHandle ctl)
  1799. {
  1800.     short    defProcID;
  1801.     ResType    defProcType;
  1802.     Str255    defProcName;
  1803.  
  1804.     *teHndl = nil;
  1805.     if (!window) return(nil);
  1806.  
  1807.     if (!gViewID) return(nil);
  1808.  
  1809.     if (!ctl)
  1810.         ctl = ((WindowPeek)window)->controlList;
  1811.     else
  1812.         ctl = (*ctl)->nextControl;
  1813.  
  1814.     while (ctl) {
  1815.         defProcID = !gViewID;
  1816.         GetResInfo((*ctl)->contrlDefProc, &defProcID, &defProcType, defProcName);
  1817.         if (defProcID == gViewID) {
  1818.             *teHndl = (TEHandle)GetCRefCon(ctl);
  1819.             break;
  1820.         }
  1821.         ctl = (*ctl)->nextControl;
  1822.     }
  1823.  
  1824.     return(ctl);
  1825. }
  1826.  
  1827.  
  1828.  
  1829. static ControlHandle    dummyCTENext(WindowPtr window, TEHandle *teHndl, ControlHandle ctl)
  1830. {
  1831. #pragma unused (window, ctl)
  1832.     *teHndl = nil;
  1833.     return(nil);
  1834. }
  1835.  
  1836.  
  1837.  
  1838. /*****************************************************************************/
  1839.  
  1840.  
  1841.  
  1842. /* Return the number of lines of text.  This is because there is a bug in
  1843. ** TextEdit where the number of lines returned is incorrect if the text
  1844. ** ends with a c/r.  This function adjusts for this bug. */
  1845.  
  1846. #pragma segment Controls
  1847. short    CTENumTextLines(TEHandle teHndl)
  1848. {
  1849.     short    lines;
  1850.     char    *cptr;
  1851.  
  1852.     if (!teHndl) return(0);
  1853.  
  1854.     lines = (*teHndl)->nLines;
  1855.  
  1856.     cptr = *((*teHndl)->hText);        /* Pointer to first TextEdit character. */
  1857.     if (cptr[(*teHndl)->teLength - 1] == kCrChar)
  1858.         ++lines;
  1859.             /* Since nLines isn’t right if the last character is a return,
  1860.             ** check for that case and fix it. */
  1861.  
  1862.     return(lines);
  1863. }
  1864.  
  1865.  
  1866.  
  1867. static short    dummyCTENumTextLines(TEHandle teHndl)
  1868. {
  1869. #pragma unused (teHndl)
  1870.     return(0);
  1871. }
  1872.  
  1873.  
  1874.  
  1875. /*****************************************************************************/
  1876.  
  1877.  
  1878.  
  1879. /* Return the number of text lines in the view area. */
  1880.  
  1881. #pragma segment Controls
  1882. short    CTENumViewLines(TEHandle teHndl)
  1883. {
  1884.     short    viewHeight;
  1885.  
  1886.     if (!teHndl) return(0);
  1887.  
  1888.     viewHeight = (*teHndl)->viewRect.bottom - (*teHndl)->viewRect.top;
  1889.     return(viewHeight / (*teHndl)->lineHeight);
  1890. }
  1891.  
  1892.  
  1893.  
  1894. static short    dummyCTENumViewLines(TEHandle teHndl)
  1895. {
  1896. #pragma unused (teHndl)
  1897.     return(0);
  1898. }
  1899.  
  1900.  
  1901.  
  1902. /*****************************************************************************/
  1903.  
  1904.  
  1905.  
  1906. /* Use this function to print the contents of a TextEdit record.  Pass it a
  1907. ** TextEdit handle, a pointer to a text offset, and a pointer to a rect to
  1908. ** print the text in.  The offset should be initialized to what character
  1909. ** in the TextEdit record you wish to start printing at (most likely 0).
  1910. ** The print function prints as much text as will fit in the rect, and
  1911. ** then updates the offset to tell you what is the first character that didn't
  1912. ** print.  You can then call the print function again with another rect with
  1913. ** this new offset, and it will print the text starting at the new offset.
  1914. ** This method is very useful when a single TextEdit record is longer than a
  1915. ** single page, and you wish the text to break at the end of the page.
  1916. ** The bottom of rect is also updated, along with the offset.  The bottom edge
  1917. ** of the rect is changed to reflect the actual bottom of the text printed.
  1918. ** This is useful because the rect passed in didn't necessarily hold an
  1919. ** integer number of lines of text.  The bottom of the rect is adjusted so
  1920. ** it exactly holds complete lines of text.
  1921. ** It is also possible that the rect could hold substantially more lines of
  1922. ** text than there are remaining.  Again, in this situation, the bottom of
  1923. ** rect is adjusted so that the rect tightly bounds the text printed.
  1924. ** The remaining piece of information passed back is an indicator that the
  1925. ** text through the end of the TextEdit record was printed.  When the end
  1926. ** of the text is reached, the offset for the next text to be printed is
  1927. ** returned as -1.  This indicates that processing of the TextEdit record
  1928. ** is complete. */
  1929.  
  1930. #pragma segment Controls
  1931. OSErr    CTEPrint(TEHandle teHndl, short *teOffset, Rect *teRct)
  1932. {
  1933.     short        len, offset, numLines, rctHeight, rctLines, h;
  1934.     Handle        hText, keepHText;
  1935.     Rect        keepDestRect, keepViewRect;
  1936.     WindowPtr    tePort, printPort;
  1937.  
  1938.     if (!teHndl) return(noErr);
  1939.  
  1940.     len = (*teHndl)->teLength;
  1941.     if (!(keepHText = NewHandle(len))) return(memFullErr);
  1942.  
  1943.     if ((offset = *teOffset) >= len) {
  1944.         *teOffset = -1;                    /* We are offset further than we have text. */
  1945.         teRct->bottom = teRct->top;        /* Empty rect, since no text to draw.  */
  1946.         return(noErr);                    /* Just say that we have no more text. */
  1947.     }
  1948.  
  1949.     BlockMove(*(hText = (*teHndl)->hText), *keepHText, len);
  1950.     keepDestRect = (*teHndl)->destRect;
  1951.     keepViewRect = (*teHndl)->viewRect;
  1952.     tePort = (*teHndl)->inPort;
  1953.         /* Cache some information from the TextEdit record. */
  1954.  
  1955.     BlockMove(*hText + offset, *hText, len - offset);
  1956.     SetHandleSize(hText, (*teHndl)->teLength = len - offset);
  1957.         /* Throw out the characters that have already been printed. */
  1958.  
  1959.     GetPort(&printPort);
  1960.     (*teHndl)->inPort = printPort;
  1961.     (*teHndl)->destRect = (*teHndl)->viewRect = *teRct;
  1962.     TECalText(teHndl);
  1963.         /* Install the print rect into the TextEdit record and then
  1964.         ** rewrap the unprinted text into this rectangle.  The text
  1965.         ** is now formatted correctly to print this rect's worth. */
  1966.  
  1967.     numLines  = CTENumTextLines(teHndl);
  1968.     rctHeight = teRct->bottom - teRct->top;
  1969.     rctLines  = rctHeight / (h = (*teHndl)->lineHeight);
  1970.  
  1971.     if (rctLines > numLines)
  1972.         rctLines = numLines;
  1973.     teRct->bottom = teRct->top + rctLines * h;
  1974.     (*teHndl)->destRect = (*teHndl)->viewRect = *teRct;
  1975.         /* We now have the minimum rectangle to hold as much of the text
  1976.         ** as would fit into the rectangle passed in.  The final calc on
  1977.         ** the rect prevents partial lines from being drawn. */
  1978.  
  1979.     TEUpdate(teRct, teHndl);        /* Draw this portion of the text. */
  1980.  
  1981.     if (rctLines == numLines)
  1982.         *teOffset = -1;
  1983.             /* Printed the last of the text for this record. */
  1984.     else
  1985.         *teOffset = (*teHndl)->lineStarts[rctLines] + offset;
  1986.             /* Offset to the first character not printed. */
  1987.  
  1988.     SetHandleSize(hText, len);
  1989.     BlockMove(*keepHText, *hText, len);
  1990.     DisposeHandle(keepHText);
  1991.     (*teHndl)->teLength = len;
  1992.     (*teHndl)->inPort   = tePort;
  1993.     (*teHndl)->destRect = keepDestRect;
  1994.     (*teHndl)->viewRect = keepViewRect;
  1995.     TECalText(teHndl);        /* Restore the TextEdit record to the way it was. */
  1996.     return(noErr);            /* Everything worked. */
  1997. }
  1998.  
  1999.  
  2000.  
  2001. static OSErr    dummyCTEPrint(TEHandle teHndl, short *teOffset, Rect *teRct)
  2002. {
  2003. #pragma unused (teHndl)
  2004.     teRct->bottom = teRct->top;
  2005.     *teOffset = -1;
  2006.     return(noErr);
  2007. }
  2008.  
  2009.  
  2010.  
  2011. /*****************************************************************************/
  2012.  
  2013.  
  2014.  
  2015. /* Return if the TextEdit control is read/write (true) or read-only (false). */
  2016.  
  2017. #pragma segment Controls
  2018. Boolean    CTEReadOnly(TEHandle teHndl)
  2019. {
  2020.     if (!teHndl) return(false);
  2021.     if ((*teHndl)->caretHook == ASMNOCARET) return(true);
  2022.     return(false);
  2023. }
  2024.  
  2025.  
  2026.  
  2027. static Boolean    dummyCTEReadOnly(TEHandle teHndl)
  2028. {
  2029. #pragma unused (teHndl)
  2030.     return(false);
  2031. }
  2032.  
  2033.  
  2034.  
  2035. /*****************************************************************************/
  2036.  
  2037.  
  2038.  
  2039. /* Return the control handle for the TextEdit control's scrollbar, either
  2040. ** vertical or horizontal.  If the scrollbar doesn't, nil is returned. */
  2041.  
  2042. #pragma segment Controls
  2043. ControlHandle    CTEScrollFromTE(TEHandle teHndl, Boolean vertScroll)
  2044. {
  2045.     ControlHandle    viewCtl;
  2046.  
  2047.     if (!(viewCtl = CTEViewFromTE(teHndl))) return(nil);
  2048.  
  2049.     return(CTEScrollFromView(viewCtl, vertScroll));
  2050. }
  2051.  
  2052.  
  2053.  
  2054. static ControlHandle    dummyCTEScrollFromTE(TEHandle teHndl, Boolean vertScroll)
  2055. {
  2056. #pragma unused (teHndl, vertScroll)
  2057.     return(nil);
  2058. }
  2059.  
  2060.  
  2061.  
  2062. /*****************************************************************************/
  2063.  
  2064.  
  2065.  
  2066. /* Return the control handle for the scrollbar related to the view control,
  2067. ** either horizontal or vertical.  If the scrollbar doesn't exist, return nil. */
  2068.  
  2069. #pragma segment Controls
  2070. ControlHandle    CTEScrollFromView(ControlHandle viewCtl, Boolean vertScroll)
  2071. {
  2072.     ControlHandle    ctl;
  2073.     WindowPtr        window;
  2074.     Boolean            vert;
  2075.  
  2076.     if (!viewCtl) return(nil);
  2077.  
  2078.     window = (*viewCtl)->contrlOwner;
  2079.     ctl    = ((WindowPeek)window)->controlList;
  2080.  
  2081.     for (; ctl;) {
  2082.         if ((ControlHandle)GetCRefCon(ctl) == viewCtl) {
  2083.             vert = false;
  2084.             if ((*ctl)->contrlRect.right == (*ctl)->contrlRect.left + 16)
  2085.                 vert = true;
  2086.             if (vert == vertScroll) return(ctl);
  2087.         }
  2088.         ctl = (*ctl)->nextControl;
  2089.     }
  2090.     return(nil);
  2091. }
  2092.  
  2093.  
  2094.  
  2095. static ControlHandle    dummyCTEScrollFromView(ControlHandle viewCtl, Boolean vertScroll)
  2096. {
  2097. #pragma unused (viewCtl, vertScroll)
  2098.     return(nil);
  2099. }
  2100.  
  2101.  
  2102.  
  2103. /*****************************************************************************/
  2104.  
  2105.  
  2106.  
  2107. /* A TextEdit control can have an optional key filter, which is called whenever
  2108. ** CTEKey() is called.  If you pass in nil, then the filtering is turned off.
  2109. ** This allows individual TextEdit controls to handle their own filtering.
  2110. ** The filter procedure is of the form:
  2111. **     Boolean (*CTEKeyFilterProcPtr)(TEHandle teHndl, EventRecord *event, Boolean *handled);
  2112. ** If true is returned, then CTEKey() is aborted, and the value in "handled" is
  2113. ** returned.  By having a separate abort value and return value, you can determine
  2114. ** if processing of the event should be continued or not, independent of whether
  2115. ** or not you aborted CTEKey(). */
  2116.  
  2117. #pragma segment Controls
  2118. void    CTESetKeyFilter(TEHandle teHndl, CTEKeyFilterProcPtr proc)
  2119. {
  2120.     ControlHandle    viewCtl;
  2121.     CTEDataHndl        teData;
  2122.  
  2123.     if (!(viewCtl = CTEViewFromTE(teHndl))) return;
  2124.     teData = (CTEDataHndl)(*viewCtl)->contrlData;
  2125.     (*teData)->keyFilter = proc;
  2126. }
  2127.  
  2128.  
  2129.  
  2130. static void    dummyCTESetKeyFilter(TEHandle teHndl, CTEKeyFilterProcPtr proc)
  2131. {
  2132. #pragma unused (teHndl, proc)
  2133. }
  2134.  
  2135.  
  2136.  
  2137. /*****************************************************************************/
  2138.  
  2139.  
  2140.  
  2141. /* Select a range of text.  TESetSelect can't be used alone because it doesn't
  2142. ** update the scrollbars.  This function calls TESetSelect, and then fixes up
  2143. ** the scrollbars. */
  2144.  
  2145. #pragma segment Controls
  2146. void    CTESetSelect(short start, short end, TEHandle teHndl)
  2147. {
  2148.     WindowPtr        oldPort;
  2149.     ControlHandle    viewCtl;
  2150.     CTEDataHndl        teData;
  2151.  
  2152.     if (teHndl) {
  2153.         GetPort(&oldPort);
  2154.         SetPort((*teHndl)->inPort);
  2155.         TESetSelect(start, end, teHndl);
  2156.         CTEAdjustScrollValues(teHndl);
  2157.         if (viewCtl = CTEViewFromTE(teHndl)) {
  2158.             teData = (CTEDataHndl)(*viewCtl)->contrlData;
  2159.             (*teData)->newUndo = true;
  2160.         }
  2161.         SetPort(oldPort);
  2162.     }
  2163. }
  2164.  
  2165.  
  2166.  
  2167. static void    dummyCTESetSelect(short start, short end, TEHandle teHndl)
  2168. {
  2169. #pragma unused (start, end, teHndl)
  2170. }
  2171.  
  2172.  
  2173.  
  2174. /*****************************************************************************/
  2175.  
  2176.  
  2177.  
  2178. /* Show the designated TextEdit control and related scrollbars. */
  2179.  
  2180. #pragma segment Controls
  2181. void    CTEShow(TEHandle teHndl)
  2182. {
  2183.     ControlHandle    viewCtl, scrollCtl;
  2184.     short            i;
  2185.  
  2186.     if (viewCtl = CTEViewFromTE(teHndl)) {
  2187.         ShowControl(viewCtl);
  2188.         for (i = 0; i < 2; i++) {
  2189.             scrollCtl = CTEScrollFromView(viewCtl, i);
  2190.             if (scrollCtl)
  2191.                 ShowControl(scrollCtl);
  2192.         }
  2193.     }
  2194. }
  2195.  
  2196.  
  2197.  
  2198. static void    dummyCTEShow(TEHandle teHndl)
  2199. {
  2200. #pragma unused (teHndl)
  2201. }
  2202.  
  2203.  
  2204.  
  2205. /*****************************************************************************/
  2206.  
  2207.  
  2208.  
  2209. /* This function is used to resize a TextEdit control.  Pass it the TextEdit
  2210. ** record to resize, plus the new horizontal and vertical size.  It will
  2211. ** resize the TextEdit control, realign the text, if necessary, plus it will
  2212. ** resize and adjust any scrollbars the TextEdit control may have.  All areas
  2213. ** that need updating are cleared and invalidated. */
  2214.  
  2215. #pragma segment Controls
  2216. void    CTESize(TEHandle teHndl, short newH, short newV, Boolean newDest)
  2217. {
  2218.     WindowPtr        oldPort;
  2219.     Rect            viewRct, brdrRct, teViewRct, oldTeViewRct, rct;
  2220.     short            i, dx, dy, mode;
  2221.     ControlHandle    viewCtl, ctl[2];
  2222.     CTEDataHndl        teData;
  2223.     RgnHandle        rgn1, rgn2;
  2224.  
  2225.     if (!(viewCtl = CTEViewFromTE(teHndl))) return;
  2226.  
  2227.     GetPort(&oldPort);
  2228.     SetPort((*teHndl)->inPort);
  2229.  
  2230.     viewRct = (*viewCtl)->contrlRect;
  2231.     teData  = (CTEDataHndl)(*viewCtl)->contrlData;
  2232.     brdrRct = (*teData)->brdrRect;
  2233.     mode    = (*teData)->mode;
  2234.  
  2235.     if (mode & (cteVScrollLessGrow - cteVScroll + cteHScrollLessGrow - cteHScroll)) {
  2236.         rct = viewRct;
  2237.         rct.top  = rct.bottom - 16;
  2238.         rct.left = rct.right - 16;
  2239.         if (mode & (cteVScrollLessGrow - cteVScroll))
  2240.             OffsetRect(&rct, 16, 0);
  2241.         if (mode & (cteHScrollLessGrow - cteHScroll))
  2242.             OffsetRect(&rct, 0, 16);
  2243.         EraseRect(&rct);
  2244.         InvalRect(&rct);
  2245.     }        /* Erase the old grow box, if we have one. */
  2246.  
  2247.     dx = newH - (viewRct.right  - viewRct.left);
  2248.     dy = newV - (viewRct.bottom - viewRct.top);
  2249.  
  2250.     for (i = 0; i < 2; ++i) {
  2251.         if (ctl[i] = CTEScrollFromView(viewCtl, i)) {
  2252.             rct = (*ctl[i])->contrlRect;
  2253.             if (i) {
  2254.                 HideControl(ctl[i]);
  2255.                 SizeControl(ctl[i], rct.right - rct.left, rct.bottom - rct.top + dy);
  2256.                 MoveControl(ctl[i], rct.left + dx, rct.top);
  2257.             }
  2258.             else {
  2259.                 HideControl(ctl[i]);
  2260.                 SizeControl(ctl[i], rct.right - rct.left + dx, rct.bottom - rct.top);
  2261.                 MoveControl(ctl[i], rct.left, rct.top + dy);
  2262.             }
  2263.         }
  2264.     }        /* Reposition the scrollbars, if we have any. */
  2265.  
  2266.     InvalRect(&viewRct);        /* Resize our view control. */
  2267.     viewRct.right  += dx;
  2268.     viewRct.bottom += dy;
  2269.     (*viewCtl)->contrlRect = viewRct;
  2270.     InvalRect(&viewRct);
  2271.  
  2272.     InvalRect(&brdrRct);        /* Resize our border rect. */
  2273.     brdrRct.right  += dx;
  2274.     brdrRct.bottom += dy;
  2275.     (*teData)->brdrRect = brdrRct;
  2276.     InvalRect(&brdrRct);
  2277.  
  2278.     teViewRct = oldTeViewRct = (*teHndl)->viewRect;        /* Resize TextEdit viewRect. */
  2279.     teViewRct.right  += dx;
  2280.     teViewRct.bottom += dy;
  2281.     (*teHndl)->viewRect = teViewRct;
  2282.  
  2283.     if (newDest) {        /* Resize TextEdit destRect, if so indicated. */
  2284.         (*teHndl)->destRect.right  += dx;
  2285.         (*teHndl)->destRect.bottom += dy;
  2286.         TECalText(teHndl);
  2287.         EraseRect(&oldTeViewRct);        /* Redraw the whole thing if destRect changes. */
  2288.         InvalRect(&oldTeViewRct);
  2289.     }
  2290.  
  2291.     rgn1 = NewRgn();
  2292.     rgn2 = NewRgn();
  2293.     RectRgn(rgn1, &oldTeViewRct);        /* Clear old areas if we are shrinking. */
  2294.     RectRgn(rgn2, &teViewRct);
  2295.     DiffRgn(rgn1, rgn2, rgn2);
  2296.     EraseRgn(rgn2);
  2297.     InvalRgn(rgn2);
  2298.     RectRgn(rgn2, &teViewRct);            /* Update new areas if we are growing. */
  2299.     DiffRgn(rgn2, rgn1, rgn2);
  2300.     InvalRgn(rgn2);
  2301.     DisposeRgn(rgn1);
  2302.     DisposeRgn(rgn2);
  2303.  
  2304.     for (i = 0; i < 2; ++i)
  2305.         if (ctl[i])
  2306.             ShowControl(ctl[i]);
  2307.                 /* Show the controls in their new position. */
  2308.  
  2309.     if (mode & (cteVScrollLessGrow - cteVScroll + cteHScrollLessGrow - cteHScroll)) {
  2310.         rct = viewRct;
  2311.         rct.top  = rct.bottom - 16;
  2312.         rct.left = rct.right - 16;
  2313.         if (mode & (cteVScrollLessGrow - cteVScroll))
  2314.             OffsetRect(&rct, 16, 0);
  2315.         if (mode & (cteHScrollLessGrow - cteHScroll))
  2316.             OffsetRect(&rct, 0, 16);
  2317.         EraseRect(&rct);
  2318.         InvalRect(&rct);
  2319.     }
  2320.  
  2321.     CTEAdjustTEBottom(teHndl);
  2322.     CTEAdjustScrollValues(teHndl);
  2323.  
  2324.     SetPort(oldPort);
  2325. }
  2326.  
  2327.  
  2328.  
  2329. static void    dummyCTESize(TEHandle teHndl, short newH, short newV, Boolean newDest)
  2330. {
  2331. #pragma unused (teHndl, newH, newV, newDest)
  2332. }
  2333.  
  2334.  
  2335.  
  2336. /*****************************************************************************/
  2337.  
  2338.  
  2339.  
  2340. /* Swap the TextEdit text handle with the text handle passed in. */
  2341.  
  2342. #pragma segment Controls
  2343. Handle    CTESwapText(TEHandle teHndl, Handle newText, Boolean update)
  2344. {
  2345.     WindowPtr    oldPort;
  2346.     Handle        hText;
  2347.     Rect        destRect, viewRect;
  2348.     short        oldTextLen, newTextLen;
  2349.  
  2350.     if (!teHndl) return(nil);
  2351.  
  2352.     GetPort(&oldPort);
  2353.     SetPort((*teHndl)->inPort);
  2354.  
  2355.     hText = (*teHndl)->hText;
  2356.  
  2357.     newTextLen = GetHandleSize(newText);
  2358.     oldTextLen = (*teHndl)->teLength;
  2359.     SetHandleSize(hText, oldTextLen);
  2360.         /* Just to make sure TE is behaving itself. */
  2361.  
  2362.     (*teHndl)->hText = newText;
  2363.     (*teHndl)->teLength = newTextLen;
  2364.  
  2365.     TECalText(teHndl);
  2366.  
  2367.     if (update) {
  2368.         destRect = (*teHndl)->destRect;
  2369.         viewRect = (*teHndl)->viewRect;
  2370.  
  2371.         OffsetRect(&destRect,
  2372.             viewRect.left - destRect.left,
  2373.             viewRect.top  - destRect.top);
  2374.  
  2375.         (*teHndl)->destRect = destRect;
  2376.         (*teHndl)->selStart = (*teHndl)->selEnd = 0;
  2377.  
  2378.         EraseRect(&viewRect);
  2379.         TEUpdate(&viewRect, teHndl);
  2380.     }
  2381.  
  2382.     CTEAdjustScrollValues(teHndl);
  2383.  
  2384.     SetPort(oldPort);
  2385.     return(hText);
  2386. }
  2387.  
  2388.  
  2389.  
  2390. static Handle    dummyCTESwapText(TEHandle teHndl, Handle newText, Boolean update)
  2391. {
  2392. #pragma unused (teHndl, update)
  2393.     return(newText);
  2394. }
  2395.  
  2396.  
  2397.  
  2398. /*****************************************************************************/
  2399.  
  2400.  
  2401.  
  2402. /* Return information for the currently active TextEdit control.  The currently
  2403. ** active TextEdit control is stored in gActiveTEHndl, and can be accessed
  2404. ** directly.  If gActiveTEHndl is nil, then there is no currently active one.
  2405. ** The information that we return is the viewRect and window of the active
  2406. ** TextEdit control.  This is information that could be gotten directly, but
  2407. ** this call makes it a little more convenient. */
  2408.  
  2409. #pragma segment Controls
  2410. WindowPtr    CTETargetInfo(TEHandle *teHndl, Rect *teView)
  2411. {
  2412.     SetRect(teView, 0, 0, 0, 0);
  2413.     if (!(*teHndl = gActiveTEHndl)) return(nil);
  2414.  
  2415.     *teView = (*gActiveTEHndl)->viewRect;
  2416.     return((*gActiveTEHndl)->inPort);
  2417. }
  2418.  
  2419.  
  2420.  
  2421. static WindowPtr    dummyCTETargetInfo(TEHandle *teHndl, Rect *teView)
  2422. {
  2423.     *teHndl = nil;
  2424.     SetRect(teView, 0, 0, 0, 0);
  2425.     return(nil);
  2426. }
  2427.  
  2428.  
  2429.  
  2430. /*****************************************************************************/
  2431.  
  2432.  
  2433.  
  2434. /* Perform an undo function for the TextEdit control. */
  2435.  
  2436. #pragma segment Controls
  2437. void    CTEUndo(void)
  2438. {
  2439.     TEHandle        teHndl;
  2440.     ControlHandle    viewCtl;
  2441.     CTEDataHndl        teData;
  2442.     Handle            hText, uText;
  2443.     short            oldStart, oldEnd;
  2444.  
  2445.     if (!(teHndl = gActiveTEHndl)) return;
  2446.  
  2447.     if (viewCtl = CTEViewFromTE(teHndl)) {
  2448.         teData = (CTEDataHndl)(*viewCtl)->contrlData;
  2449.         hText  = (*teHndl)->hText;
  2450.         uText  = (*teData)->undoText;
  2451.         if (!uText) return;        /* There is no undo yet.  Getting called in
  2452.                                 ** this state is actually an error, but we
  2453.                                 ** check, just in case.
  2454.                                 */
  2455.         oldStart = (*teHndl)->selStart;
  2456.         oldEnd   = (*teHndl)->selEnd;
  2457.         (*teData)->undoText = CTESwapText(teHndl, (*teData)->undoText, false);
  2458.         (*teHndl)->selStart = (*teHndl)->selEnd = 0;
  2459.         CTEUpdate(teHndl, viewCtl, false);
  2460.         CTESetSelect((*teData)->undoSelStart, (*teData)->undoSelEnd, teHndl);
  2461.             /* CTESetSelect flags it as a new undo situation for us. */
  2462.         (*teData)->undoSelStart = oldStart;
  2463.         (*teData)->undoSelEnd   = oldEnd;
  2464.     }
  2465. }
  2466.  
  2467.  
  2468.  
  2469. static void    dummyCTEUndo(void)
  2470. {
  2471. }
  2472.  
  2473.  
  2474.  
  2475. /*****************************************************************************/
  2476.  
  2477.  
  2478.  
  2479. /* Draw the TextEdit control and frame. */
  2480.  
  2481. #pragma segment Controls
  2482. void    CTEUpdate(TEHandle teHndl, ControlHandle ctl, Boolean justShowActive)
  2483. {
  2484.     WindowPtr    oldPort, tePort;
  2485.     Rect        viewRect, brdrRect;
  2486.     CTEDataHndl    teData;
  2487.     short        mode;
  2488.  
  2489.     if (teHndl) {
  2490.         if ((*ctl)->contrlVis) {
  2491.  
  2492.             GetPort(&oldPort);
  2493.             SetPort(tePort = (*teHndl)->inPort);
  2494.  
  2495.             teData   = (CTEDataHndl)(*ctl)->contrlData;
  2496.             mode     = (*teData)->mode;
  2497.             viewRect = (*teHndl)->viewRect;
  2498.             brdrRect = (*teData)->brdrRect;
  2499.             if (!(mode & cteNoBorder))
  2500.                 FrameRect(&brdrRect);
  2501.  
  2502.             if (!justShowActive) {
  2503.                 EraseRect(&viewRect);
  2504.                 TEUpdate(&viewRect, teHndl);
  2505.             }
  2506.     
  2507.             if (mode & cteShowActive) {
  2508.                 InsetRect(&brdrRect, -3, -3);
  2509.                 if (mode & cteVScroll)
  2510.                     brdrRect.right  += 15;
  2511.                 if (mode & cteHScroll)
  2512.                     brdrRect.bottom += 15;
  2513.                 if ((!((WindowPeek)tePort)->hilited) || (teHndl != gActiveTEHndl))
  2514.                     PenPat((ConstPatternParam)&qd.white);
  2515.                 PenSize(2, 2);
  2516.                 FrameRect(&brdrRect);
  2517.                 PenNormal();
  2518.             }
  2519.  
  2520.             SetPort(oldPort);
  2521.         }
  2522.     }
  2523. }
  2524.  
  2525.  
  2526.  
  2527. static void    dummyCTEUpdate(TEHandle teHndl, ControlHandle ctl, Boolean justShowActive)
  2528. {
  2529. #pragma unused (teHndl, ctl, justShowActive)
  2530. }
  2531.  
  2532.  
  2533.  
  2534. /*****************************************************************************/
  2535.  
  2536.  
  2537.  
  2538. /* Return the control handle for the view control that owns the TextEdit
  2539. ** record.  Use this to find the view to do customizations such as changing
  2540. ** the update procedure for this TextEdit control. */
  2541.  
  2542. #pragma segment Controls
  2543. ControlHandle    CTEViewFromTE(TEHandle teHndl)
  2544. {
  2545.     WindowPtr        window;
  2546.     ControlHandle    viewCtl;
  2547.     TEHandle        te;
  2548.  
  2549.     if (!teHndl) return(nil);
  2550.  
  2551.     window = (WindowPtr)(*teHndl)->inPort;
  2552.  
  2553.     for (viewCtl = nil;;) {
  2554.  
  2555.         viewCtl = CTENext(window, &te, viewCtl);
  2556.         if ((!viewCtl) || (te == teHndl)) return(viewCtl);
  2557.     }
  2558. }
  2559.  
  2560.  
  2561.  
  2562. static ControlHandle    dummyCTEViewFromTE(TEHandle teHndl)
  2563. {
  2564. #pragma unused (teHndl)
  2565.     return(nil);
  2566. }
  2567.  
  2568.  
  2569.  
  2570. /*****************************************************************************/
  2571.  
  2572.  
  2573.  
  2574. /* Call this when a window with TextEdit controls is being activated.  This
  2575. ** will make the TextEdit control that was last active in this window the
  2576. ** active TextEdit control again.  If it can't find one that was the last
  2577. ** active control, it makes the first one it finds the active control. */
  2578.  
  2579. #pragma segment Controls
  2580. Boolean    CTEWindActivate(WindowPtr window)
  2581. {
  2582.     short            hilite, scrollNum;
  2583.     Boolean            oneIsActive;
  2584.     ControlHandle    viewCtl, scrollCtl;
  2585.     TEHandle        te;
  2586.     CTEDataHndl        teData;
  2587.  
  2588.     if (!window) return(false);
  2589.  
  2590.     hilite = 255;
  2591.     if (((WindowPeek)window)->hilited)
  2592.         hilite = 0;
  2593.  
  2594.     for (oneIsActive = false, viewCtl = nil;;) {
  2595.  
  2596.         viewCtl = CTENext(window, &te, viewCtl);
  2597.         if (!viewCtl) break;
  2598.             /* The only way out of the loop. */
  2599.  
  2600.         teData = (CTEDataHndl)(*viewCtl)->contrlData;
  2601.         if ((*teData)->mode & cteActive) {
  2602.             if (!hilite) {
  2603.                 CTEActivate(true, te);
  2604.                 oneIsActive = true;
  2605.             }
  2606.             else CTEActivate(false, te);
  2607.         }
  2608.  
  2609.         for (scrollNum = 0; scrollNum < 2; ++scrollNum) {
  2610.             scrollCtl = CTEScrollFromTE(te, scrollNum);
  2611.             if (scrollCtl)
  2612.                 HiliteControl(scrollCtl, hilite);
  2613.         }
  2614.     }
  2615.  
  2616.     return(oneIsActive);
  2617. }
  2618.  
  2619.  
  2620.  
  2621. static Boolean    dummyCTEWindActivate(WindowPtr window)
  2622. {
  2623. #pragma unused (window)
  2624.     return(false);
  2625. }
  2626.  
  2627.  
  2628.  
  2629. /*****************************************************************************/
  2630.  
  2631.  
  2632.  
  2633. /* This function is called after an edit to make sure that there is no extra
  2634. ** white space at the bottom of the viewRect.  If there are blank lines at
  2635. ** the bottom of the viewRect, and there is text scrolled off the top of the
  2636. ** viewRect, then the TextEdit control is scrolled to fill this space, or as
  2637. ** much of it as possible. */
  2638.  
  2639. #pragma segment Controls
  2640. void    CTEAdjustTEBottom(TEHandle teHndl)
  2641. {
  2642.     Rect    destRect, viewRect;
  2643.     short    botDiff, topDiff;
  2644.  
  2645.     destRect = (*teHndl)->destRect;
  2646.     viewRect = (*teHndl)->viewRect;
  2647.     destRect.bottom = destRect.top + CTEDocHeight(teHndl);
  2648.  
  2649.     botDiff = viewRect.bottom - destRect.bottom;
  2650.     if (botDiff > 0) {
  2651.         topDiff = viewRect.top - destRect.top;
  2652.         if (botDiff > topDiff)
  2653.             botDiff = topDiff;
  2654.         if (botDiff)
  2655.             TEScroll(0, botDiff, teHndl);
  2656.     }
  2657.  
  2658. }
  2659.  
  2660.  
  2661.  
  2662. static void    dummyCTEAdjustTEBottom(TEHandle teHndl)
  2663. {
  2664. #pragma unused (teHndl)
  2665. }
  2666.  
  2667.  
  2668.  
  2669. /*****************************************************************************/
  2670.  
  2671.  
  2672.  
  2673. /* Bring the scrollbar values up to date with the current document position
  2674. ** and length. */
  2675.  
  2676. #pragma segment Controls
  2677. void    CTEAdjustScrollValues(TEHandle teHndl)
  2678. {
  2679.     short            scrollNum;
  2680.     ControlHandle    scrollCtl;
  2681.  
  2682.     for (scrollNum = 0; scrollNum < 2; ++scrollNum) {
  2683.         scrollCtl = CTEScrollFromTE(teHndl, scrollNum);
  2684.         if (scrollCtl)
  2685.             AdjustOneScrollValue(teHndl, scrollCtl, scrollNum);
  2686.     }
  2687. }
  2688.  
  2689.  
  2690.  
  2691. static void    dummyCTEAdjustScrollValues(TEHandle teHndl)
  2692. {
  2693. #pragma unused (teHndl)
  2694. }
  2695.  
  2696.  
  2697.  
  2698. /*****************************************************************************/
  2699. /*****************************************************************************/
  2700.  
  2701.  
  2702.  
  2703. #pragma segment Controls
  2704. static pascal void    VActionProc(ControlHandle scrollCtl, short part)
  2705. {
  2706.     short        lineHeight, delta, value, teOffset;
  2707.     short        oldValue, max;
  2708.     TEHandle    te;
  2709.     
  2710.     if (part) {                        /* If it was actually in the control. */
  2711.  
  2712.         te = gActiveTEHndl;
  2713.         lineHeight = (*te)->lineHeight;
  2714.  
  2715.         switch (part) {
  2716.             case inUpButton:
  2717.             case inDownButton:        /* One line. */
  2718.                 delta = lineHeight;
  2719.                 break;
  2720.             case inPageUp:            /* One page. */
  2721.             case inPageDown:
  2722.                 delta = (*te)->viewRect.bottom - (*te)->viewRect.top;
  2723.                 if (delta > lineHeight)
  2724.                     delta -= lineHeight;
  2725.                 break;
  2726.         }
  2727.         if ( (part == inUpButton) || (part == inPageUp) )
  2728.             delta = -delta;        /* Reverse direction for an upper. */
  2729.  
  2730.         value = (oldValue = GetCtlValue(scrollCtl)) + delta;
  2731.         if (value < 0)
  2732.             value = 0;
  2733.         if (value > (max = GetCtlMax(scrollCtl)))
  2734.             value = max;
  2735.  
  2736.         if (value != oldValue) {
  2737.             SetCtlValue(scrollCtl, value);
  2738.             teOffset = (*te)->viewRect.top - (*te)->destRect.top;
  2739.             if (value -= teOffset)
  2740.                 TEScroll(0, -value, te);
  2741.         }
  2742.     }
  2743. }
  2744.  
  2745.  
  2746.  
  2747. /*****************************************************************************/
  2748.  
  2749.  
  2750.  
  2751. #pragma segment Controls
  2752. static pascal void    HActionProc(ControlHandle scrollCtl, short part)
  2753. {
  2754.     short        lineHeight, delta, value, teOffset;
  2755.     short        oldValue, max;
  2756.     TEHandle    te;
  2757.     
  2758.     if (part) {                        /* If it was actually in the control. */
  2759.  
  2760.         te = gActiveTEHndl;
  2761.         lineHeight = (*te)->lineHeight;
  2762.  
  2763.         switch (part) {
  2764.             case inUpButton:
  2765.             case inDownButton:        /* One line. */
  2766.                 delta = lineHeight;
  2767.                 break;
  2768.             case inPageUp:            /* One page. */
  2769.             case inPageDown:
  2770.                 delta = (*te)->viewRect.right - (*te)->viewRect.left;
  2771.                 if (delta > lineHeight)
  2772.                     delta -= lineHeight;
  2773.                 break;
  2774.         }
  2775.         if ( (part == inUpButton) || (part == inPageUp) )
  2776.             delta = -delta;        /* Reverse direction for an upper. */
  2777.  
  2778.         value = (oldValue = GetCtlValue(scrollCtl)) + delta;
  2779.         if (value < 0)
  2780.             value = 0;
  2781.         if (value > (max = GetCtlMax(scrollCtl)))
  2782.             value = max;
  2783.  
  2784.         if (value != oldValue) {
  2785.             SetCtlValue(scrollCtl, value);
  2786.             teOffset = (*te)->viewRect.left - (*te)->destRect.left;
  2787.             if (value -= teOffset)
  2788.                 TEScroll(-value, 0, te);
  2789.         }
  2790.     }
  2791. }
  2792.  
  2793.  
  2794.  
  2795. /*****************************************************************************/
  2796.  
  2797.  
  2798.  
  2799. /* Bring one scrollbar value up to date with the current document position
  2800. ** and length. */
  2801.  
  2802. #pragma segment Controls
  2803. static void    AdjustOneScrollValue(TEHandle teHndl, ControlHandle ctl, Boolean vert)
  2804. {
  2805.     Boolean    front;
  2806.     short    textPix, viewPix;
  2807.     short    max, oldMax, value, oldValue;
  2808.  
  2809.     front = ((WindowPeek)(*ctl)->contrlOwner)->hilited;
  2810.  
  2811.     oldValue = GetCtlValue(ctl);
  2812.     oldMax   = GetCtlMax(ctl);
  2813.  
  2814.     if (vert) {
  2815.         textPix = CTEDocHeight(teHndl);
  2816.         viewPix = (*teHndl)->viewRect.bottom - (*teHndl)->viewRect.top;
  2817.     }
  2818.     else {
  2819.         textPix = (*teHndl)->destRect.right - (*teHndl)->destRect.left;
  2820.         viewPix = (*teHndl)->viewRect.right - (*teHndl)->viewRect.left;
  2821.     }
  2822.     max = textPix - viewPix;
  2823.  
  2824.     if (max < 0)
  2825.         max = 0;
  2826.     if (max != oldMax) {
  2827.         if (front)
  2828.             SetCtlMax(ctl, max);
  2829.         else
  2830.             (*ctl)->contrlMax = max;
  2831.     }
  2832.  
  2833.     if (vert)
  2834.         value = (*teHndl)->viewRect.top  - (*teHndl)->destRect.top;
  2835.     else
  2836.         value = (*teHndl)->viewRect.left - (*teHndl)->destRect.left;
  2837.  
  2838.     if (value < 0)
  2839.         value = 0;
  2840.     if (value > max)
  2841.         value = max;
  2842.     if (value != oldValue) {
  2843.         if (front)
  2844.             SetCtlValue(ctl, value);
  2845.         else
  2846.             (*ctl)->contrlValue = value;
  2847.     }
  2848. }
  2849.  
  2850.  
  2851.  
  2852.